adobe / aem-headless-client-js

AEM Headless Client for JavaScript
Apache License 2.0
25 stars 12 forks source link

Handle 401 with no body #54

Closed gregal closed 1 year ago

gregal commented 1 year ago

Expected Behaviour

The headless client does not seem to handle well the situation where status 401 is returned by AEM with no response body.

As seen in the screenshot below, there is a situation in which AEM responds just that way:

aem-response-401

It's actually a behavior from AEM that I do not quite understand.

All the above was just to provide the context in which I perceive the issue. It is of course not an bug/feature in the headless client.

But when that happens, it would be great if my application would have the means to detect that a certain request failed because the token used got expired. Or in a broader way, that the request failed due to authentication.

Perhaps if the headless client would throw something like a specialized Error (IDK, AuthenticationError?), then my application could decide to renew the token and then trying again.

   const client = new AEMHeadless({
     serviceURL,
     auth: this.accessToken,
   })

  try {
    const response = await client.runPersistedQuery(path)
  } catch (e) {
    if (e instanceof AuthenticationError) {
      // conclude token expired -> renew token
      // retry request
    }
  }

Actual Behaviour

My code catches an Error thrown by the headless client. I can detect that it is an extension of AioCoreSDKError with code = RESPONSE_ERROR.

Message says:

[AEMHeadless:RESPONSE_ERROR] There was a problem parsing response data: invalid json response body at https://publish-....adobeaemcloud.com/graphql/execute.json/..... reason: Unexpected end of JSON input.

By analyzing the code in AEMHeadless.__handleRequest, I can see that a RESPONSE_ERROR could be yielded in several different situations, so that doesn't help my app decide what to do next.

But by knowing the specific case (401 without body), as per my analysis, the Error is thrown in scenario 2.1 below, specifically when trying to invoke .json() on a response that is not a valid JSON object:

    let apiError
    // 2. Handle Response error
    if (!response.ok) {
      try {
        // 2.1 Check if custom error is returned
        apiError = await response.json()
      } catch (error) {
        // 2.3 Response error: Couldn't parse JSON - no error defined in API response
        throw new RESPONSE_ERROR({
          sdkDetails: {
            serviceURL: this.serviceURL,
            endpoint
          },
          messageValues: error.message
        })
      }
    }

Reproduce Scenario (including but not limited to)

In our scenario, we have secured content (using CUG's) which require authentication through the GraphQL API to be returned.

Then we use

Steps to Reproduce

  1. Setup CUG security in some assets folder.
  2. Retrieve an access token using @adobe/jwt-auth.
  3. Use it normally.
  4. Wait for it to be considered invalid by AEM (could take several days).
  5. See the requests fail.

Platform and Version

Both Linux and Windows. AEMHeadless 3.3.0

easingthemes commented 1 year ago

Hi, I've opened a PR for response.status in error details - https://github.com/adobe/aem-headless-client-js/pull/58/files#diff-bfe9874d239014961b1ae4e89875a6155667db834a410aaaa2ebe3cf89820556R332

You can easily check if it's Auth error:

error.sdkDetails.status === 401

If you need to know exact case when error happened, then check both code and status: