api-platform / admin

A beautiful and fully-featured administration interface builder for hypermedia APIs
https://api-platform.com/docs/admin/
MIT License
482 stars 130 forks source link

Schema Analyzer calls don't have the refreshed token which causes 401 error #564

Open greggailly opened 4 months ago

greggailly commented 4 months ago

API Platform version(s) affected: api: 3.3, admin: 3.4.6

Description
When accessing a resource from the admin, several extra calls seem to be made on top of the main call fetching the data:

Screenshot 2024-05-31 at 14 11 12

These calls seem to be initiated by the Guesser Components to fetch the schema:

Screenshot 2024-05-31 at 15 35 15

These calls do include a JWT but this token is not refreshed if expired. The JWT in the "main" call is correctly updated.

How to reproduce

  1. Clone Api Platform Demo repository
  2. Remove components so that the Guesser components do their job

Possible Solution
n/a

Additional Context

const apiDocumentationParser = (session) => async () => { try { return await parseHydraDocumentationWithEnums(ENTRYPOINT, { headers: { Authorization: Bearer ${session?.accessToken}, }, }); } catch (result) { // @ts-ignore const { api, response, status } = result; if (status !== 401 || !response) { throw result; }

return {
  api,
  response,
  status,
};

} };

const AdminUI = ({ children, session }) => {

const dataProvider = useRef(); const { docType } = useContext(DocContext);

dataProvider.current = hydraDataProvider({ entrypoint: ENTRYPOINT, httpClient: (url, options = {}) => fetchHydra(url, { ...options, headers: { Authorization: Bearer ${session?.accessToken}, }, }), apiDocumentationParser: apiDocumentationParser(session), });

return docType === "hydra" ? ( <HydraAdmin requireAuth authProvider={authProvider} // @ts-ignore dataProvider={dataProvider.current} entrypoint={window.origin} i18nProvider={i18nProvider} layout={MyLayout}

{!!children && children} ) : ( <OpenApiAdmin requireAuth authProvider={authProvider} // @ts-ignore dataProvider={dataProvider.current} entrypoint={window.origin} docEntrypoint={${window.origin}/docs.json} i18nProvider={i18nProvider} layout={MyLayout}

{!!children && children} ); };

const AdminWithContext = ({ session }) => {

const [docType, setDocType] = useState( store.getItem("docType", "hydra"), );

return ( <DocContext.Provider value={{ docType, setDocType, }}>

// No Resources defined here unlike in the demo repo
</DocContext.Provider>

); };



- parseHydraDocumentationWithEnums is based on https://github.com/api-platform/admin/issues/494#issuecomment-1427746210 doesn't seem to be the source as when using the normal `parseHydraDocumentation` problem remains.
- Calls are made every time we access the resource from the admin if no ApiFilter is defined in the API
- If ApiFilter is defined in the API then calls are only made the first time we try to access the resource. If the resource is not accessed at least once before the initial token expires the problem remains 
greggailly commented 4 months ago

Issue seems to be coming from the api-doc-parser where the getParameters function is returned for each Resource with the token saved into it. Since parseHydraDocumentation is only called once when the page is loaded the JWT is never updated in the resource function call.

resource.getParameters = (): Promise<Parameter[]> => getParameters(resource, options);

https://github.com/api-platform/api-doc-parser/blob/e8b49f2b452c4a19fab5eea86ec742c8201d381f/src/hydra/parseHydraDocumentation.ts#L475-L476

greggailly commented 4 months ago

@dunglas would such a change to the getParameters callback be accepeted ? Adding a "in_options" argument to eventually override at runtime the headers that can be sent to fetch resource parameters.

resource.getParameters = (in_options: RequestInitExtended = {}): Promise<Parameter[]> =>
          getParameters(resource, {...options, ...in_options});

If yes then to be discussed, the schemaAnalyzer could be modified to take a new argument with the options.

export const resolveSchemaParameters = (schema: Resource, options: RequestInitExtended = {} ) => {
  if (!schema.parameters || !schema.getParameters) {
    return Promise.resolve([]);
  }

  return !schema.parameters.length
    ? schema.getParameters(options)
    : Promise.resolve(schema.parameters);
};

};

Options (with Authentication headers) could then either be passed down from the FilterGuesser (and other Guessers) or (less likely I guess) fetched directly in the schemaAnalyzer (using local storage maybe ?).

Unless you see another solution to have the updated tokens passed down to the getParameter requests !

papppeter commented 3 days ago

Do you have a solution? it v4 is affected.

papppeter commented 3 days ago

https://github.com/api-platform/admin/issues/578 helped me to solve my issue. i created a custom parser where i did set the proper headers for authentication. but any better idea is wellcome.