Help with 400 error on /token without an error message

I would like some help with debugging a 400 error on the /token endpoint.

We use Sentry to log errors that our users encounter, however there isn’t really any information about this error. The only thing we get is an unhandled exception with AuthApiError: No error message.

I think this error started occuring once we enabled “Refresh token” as a grant type in the application settings for our React SPA. However, I’m not really sure if that is actually what triggered it. Also we use the latest package versions, so "@okta/okta-auth-js": "^5.6.0" and "@okta/okta-react": "^6.2.0".

Does anyone have any guidance on how to debug this error or any info on what could cause it?

Anything more about the token call shown in the network tab?

What URL do you see for the authorize request and the token request? Do they both start with the same issuer URI (e.g. https://org.okta.com/oauth2/v1 or https://org.okta.com/oauth2/default/v1 or https://org.okta.com/oauth2/aus123456789/v1)?

I only see a token request for these errors it seems, which looks like this:
POST https://idm.floriday.io/oauth2/aus3testdcf2vyfs70i7/v1/token [400]

Followed by the exception:
AuthApiError: No error message

I also can’t seem to reproduce this error, so I don’t have any info from the Network tab. I’ve also noticed that it happens in some cases when the user has been inactive for several hours. So it might have something to do with refreshing the token.

Do you see anything in your system log when this failure occurs? I’m seeing some claim evaluation failures in your org, and its possible that these failures are preventing the token from being issued when its requested.

You may want to test with another custom authorization server with less custom claims to see if you can get a token back or if the issue is isolated to that specific server.

I’ve tried checking a few token errors based on user.id and the timestamp, but none of them seem to match. There are indeed a lot of user_claim_evaluation_failure warnings, however they also don’t match with any timestamps in Sentry unfortunately.

The System Log does indicate that the id_token and access_token are successfully requested, even after the claim evaluation failure.

Are there any other settings I could check before testing a different custom authorization server?

You can try making the requests manually in Postman to see if the issue is specific to the SDK or not (though the error indicates that the API is returning the error, not the SDK) and in case there are more details available when you make the request that way.

Didn’t have any success with testing via Postman, however I have now changed my access token and refresh token lifetime both to 5 minutes on our staging environment. Also the expiration time of the refresh token is 5 minutes now.

These settings aren’t really practical or anything, but this does sometimes trigger a 400 bad request on the /token endpoint. Unfortunately there is no response data in the network tab.

I also have devMode logging enabled, which shows roughly this in the console:

OKTA-AUTH-JS:updateAuthState: Event:added Status:canceled
refreshToken 

OKTA-AUTH-JS:updateAuthState: Event:undefined Status:emitted
undefined undefined

POST https://idm.staging.floriday.io/oauth2/ausmw6b47z1BnlHkw0h7/v1/token 400 (Bad Request)

OKTA-AUTH-JS:updateAuthState: Event:removed Status:emitted
accessToken 

Navigated to http://localhost:3000/signin-callback?code=...

For all events there is an accessToken, idToken or refreshToken logged. However before the 400 error there is a Event: undefined logged, with also an undefined token.

Do you know when such “undefined” events might be triggered and logged?

I haven’t checked the token lifetimes on our production environment yet, but it might be related to this 400 issue.

Edit: The values on our production environment are:
Access token lifetime: 3 hours
Refresh token lifetime: unlimited
But will expire if not used in: 7 days

Are you seeing the 400 when using the refresh token or in the initial OAuth flow to get a refresh token?

I think it happens when using the refresh token. We use the okta-react package, so I think it does all that magic for us. Also the grant type does say refresh_token for these requests.

Can you check if the refresh token is still valid when/after the SDK attempts to use it to renew the tokens by sending it to the introspect endpoint?

It took a while to get a refresh_token right after the 400 error.
The introspect endpoints returned:

{
    "active": false
}

I have also checked the new refresh_token after the page has refreshed after the 400 error, and that one does give "active: true" and such via the introspect endpoint.

So it looks like the refresh_token isn’t active anymore right after the 400 error.

Do you have refresh token rotation enabled on your application by chance?

Yes, it is enabled. I thought I read somewhere in the documentation that it should be enabled.

It is recommended for SPAs, true. The thing I was wondering is if the cause of your /400 errors is because you are attempting to re-use a refresh token, which will cause your refresh and access tokens to become invalidated.

I’m not sure how that would be occurring within the SDK unless there’s a duplicate call to renew occurring or the renew tokens aren’t getting updated correctly in the tokenManager (to contain only the most recently issued refresh token)

It took some time, but I have tested it on a way cleaner Okta setup now. And I also get the 400 error there, with a response.

{
    "error": "invalid_grant",
    "error_description": "The refresh token is invalid or expired."
}

So indeed the refresh token is expired (or invalid) for that case. The only thing I don’t understand is why it gives a readable error response in my test environment, and not in our production environment. The No error message just doesn’t make sense to me. Also this error is probably triggered because I’ve set all the token expiration times to 5 minutes again, just to see if this error would pop up.

My test environment token URL is:
https://dev-3618926.okta.com/oauth2/default/v1/token

And the request parameters were:

client_id: 0oa3py2wanBh4aN7x5d6
grant_type: refresh_token
scope: email offline_access openid profile
refresh_token: LYOQRIpD_NiBOYmHLdH5p9np90YKrSto0KvPSecHdLE

Could it be that something is wrong in the SDK with using the refresh tokens? Or is it possible that refresh tokens just expire or become invalid while using the SPA?