Unknown or invalid refresh token on Android and iOS

Greetings,

Our team is facing some problems with auth0 in mobile apps (both, Android and iOS). We have looked through a lot of topics here but couldn’t find answers to our questions.

The problem we’re facing is auth0 returns “Unknown or invalid refresh token” for some users when the refresh token is not supposed to expire.

We login our users as recommended in the documentation.

iOS
func requestWebAuthLogin(callback: @escaping (Result<Credentials, WebAuthError>) → Void) {
  Auth0
   .webAuth(client, domain) // we use our client, domain
   .scope(“openid offline_access”)
   .audience(audience) // we use our audience
   .useEphemeralSession()
   .parameters([“prompt”: “login”])
   .start { /* our callback logic  and saving credentials */ }
  }
}
Android
login(auth0) // auth0 instance with our client and domain
  .withParameters(mapOf(“prompt” to “login”))
  .withAudience(audience) // we use our audience
  .withScheme(scheme) // we use our scheme
  .withScope("openid offline_access")
  .start( /* our callback logic  and saving credentials */ )

And retrieve the token to reauthenticate them:

iOS
credentialsManager.credentials(minTTL: 6 * 60) { 
    /* our callback logic */ 
}
Android
credentialsManager.getCredentials(
    null, 
    6 * 60, 
    /* our callback logic */
)

Our refresh token configurations are:

Hi @Arthur_Dent

Welcome to the Auth0 Community!

I am sorry about the delayed response to your inquiry!

If you disable Refresh Token Rotation within the Auth0 Dashboard OR if you increase the Rotation Overlap Period, does the error go away or does it still persist?

Also, for iOS, can you try configuring your Auth0 instance with a maxRetries parameter?

const auth0 = new Auth0({
  domain: 'YOUR_DOMAIN',
  clientId: 'YOUR_CLIENT_ID',
  maxRetries: 2, // Retry up to 2 times on transient errors (iOS only)
});

It appears that the issue might be caused by a race condition where your credentialsManager is returning the 2nd refresh token after the first one has been used and they both get invalidated.

Just to confirm, the error you mentioned is the only one you receive or do you also receive an “Unsuccessful Refresh Token exchange, reused refresh token detected” error?

Kind Regards,
Nik

Greetings @nik.baleca, thank you for your response!

If you disable Refresh Token Rotation within the Auth0 Dashboard OR if you increase the Rotation Overlap Period, does the error go away or does it still persist?

We haven’t tried disabling refresh token rotation or increasing the rotation overlap period. We might try that, however it would be great to get reference in the documentation or rationale/context behind this question so that we could dig further and localise the problem.

We can provide more information around our refresh token configs.

Just to confirm, the error you mentioned is the only one you receive or do you also receive an “Unsuccessful Refresh Token exchange, reused refresh token detected” error?

We haven’t found this error in our logs.

Thanks

Any help would be appreciated.

Hi again @Arthur_Dent

I am truly sorry about the delayed reply to your last update regarding the matter! I must have missed the initial notification.

We haven’t tried disabling refresh token rotation or increasing the rotation overlap period. We might try that, however it would be great to get reference in the documentation or rationale/context behind this question so that we could dig further and localise the problem.

The error message that you are receiving would indicate the possibility of your application re-using the refresh token or that it is trying to use both refresh tokens (1st one initially issued and the one that is rotated) during a single request, either due to a race condition, absolute expiration values, reuse period settings or even custom login within your application.

You can read more about that inside this knowledge article.

Otherwise, that is why I suggested the above to see if the refresh token rotation is the cause of the behaviour that you are experiencing. I would highly recommend testing the refresh token expiration/rotation settings to make sure that they are not getting invalidated during the user’s session.

In order to provide more context about the error mentioned previously, there is a known bug/issue in regards to the Native SDKs where the team provided updates to mitigate the issue. You can read more about that here.

Can you provide some extra information on what SDKs are you using for Android and iOS respectively?

Kind Regards,
Nik

Hi again!

Since you have not replied back regarding the matter, I will be marking my previous reply as the solution.

Feel free to jump back with any additional information or post again referencing this topic!

Kind Regards,
Nik

Hello @nik.baleca ,

We tried testing with the suggested changes. Errors did occur, but we did not notice any pattern that could help us localize the issue. That said, we would like to get more details about the error we are seeing.

Could you help us with the following:

  • Can Auth0 logs tell us why a specific token was rejected?
  • Are there known issues with useEphemeralSession() and refresh tokens on iOS?
  • Are there specific Dashboard log events we should be watching?

Thanks!

Any help is appreciated!

Hi again @Arthur_Dent

I am sorry for the delayed response as I have been out of office recently and could not get back to you with a prompt reply.

  1. Yes, but sometimes indirectly. The error log itself (fertft ) will give you a high-level reason (e.g., expired vs. missing), but the true root cause (like a token being forcefully revoked) is almost always found by looking at the logs immediately preceding the error.
  2. No, there are no known bugs linking useEphemeralSession() to refresh token invalidation. This is a common red herring. Ephemeral sessions affect browser cookies, whereas refresh tokens rely entirely on backend API calls and the iOS Keychain.
    → When you enable useEphemeralSession() on iOS, it instructs the ASWebAuthenticationSession to act like an incognito tab. It prevents the login cookie from being saved to the shared Safari cookie jar.
    → Once the login is complete, Auth0 hands the Refresh Token and Access Token back to your Swift code, and the CredentialsManager locks them securely in the iOS Keychain. When your app needs a new Access Token, it makes a silent background HTTP request (/oauth/token ) directly to Auth0. This background request does not use the browser, nor does it care about browser cookies. Therefore, an ephemeral session cannot physically break a refresh token.
  3. Yes, there is a specific sequence of dashboard logs you must watch. Specifically, you need to track rrrt and resource_cleanup events to understand the lifecycle of the token.
    → When a refresh token is rejected, Auth0 logs a fertft (Failed Exchange of Refresh Token) event. If the description says “Token could not be decoded or is missing in DB,” it means Auth0 does not recognize it. This happens for three primary reasons:
  • The token was intentionally revoked.
  • The user hit the absolute device limit.
  • The token family was purged due to a perceived replay attack (Reuse Detection).

I would highly recommend following up on the issue using the Github link I have posted before in the thread and open a new issue regarding the error you are experiencing if they persist within your application to see if it is an underlying issue with the sdk or the configuration within your app since this has been a common pain point in the past regarding native applications.

Kind Regards,
Nik