Auth0 + React + Ionic + Capacitor: Random error missing refresh token on iOS

Hi All,

I’m developing a SPA using React and Ionic that is packaged and deployed as PWA in iOS with Capacitor, which also includes Ionic components. I integrated Auth0 using the SDK auth0-react, In geral seems to work fine.

However, sometimes randomly, when I open the app the refresh token seems to be gone or invalid. I’m getting error missing_refresh_token when I call getAccessTokenSilentl()just before I want to make an authenticated API call. Additionally seems strange that the isAuthenticatedmethod still returns true otherwise I won’t even be showing that view.

I’m using a custom cache implementation that uses the capacitor plugin capacitor-secure-storage-plugin@^0.12.0 to store the refresh token more securely in iOS Keychain. However the same problem was happening using localstorage in auth0 config.

I’m not sure if Auth0 SDK messing up with Refresh Tokens or iOS just randomly wiping the keychain storage of the app.

I have enabled refresh tokens in Auth0 dashboard and the Idle Refresh Token Lifetime is set to 15 days.

This my config:

 {
      domain: import.meta.env.VITE_AUTH0_DOMAIN,
      clientId: import.meta.env.VITE_AUTH0_CLIENT_ID_NATIVE,
      scope: 'openid profile email offline_access',
      cache: createSecureAuthCache(),
      useRefreshTokens: true,
      useRefreshTokensFallback: false,
      authorizationParams: {
        audience: import.meta.env.VITE_AUTH0_AUDIENCE,
        redirect_uri: import.meta.env.VITE_AUTH0_CALLBACK_URL,
      }
 }

This how I login:

 {
      await loginWithRedirect({
        async openUrl(url) {
          await Browser.open({
            url,
            windowName: '_self',
            presentationStyle: 'popover',
          });
        },
      });
    }

This is how I logout:

const returnTo = import.meta.env.VITE_AUTH0_CALLBACK_URL;
 
await logout({
        logoutParams: { returnTo },
        async openUrl(url) {
          await Browser.open({
            url,
            windowName: '_self',
            presentationStyle: 'popover',
          });
        },
      });
    }
    const setupDeepLinkListener = async () => {
      listener = await CapacitorApp.addListener('appUrlOpen', async (event) => {
        console.log('Deep link received:', event.url);

        const callbackUrl = import.meta.env.VITE_AUTH0_CALLBACK_URL;
        if (event.url.startsWith(callbackUrl)) {

          const url = event.url;

          console.log('Processing Auth0 callback with URL:', url);

          if (url.includes('state') && (url.includes('code') || url.includes('error'))) {
            try {
              await handleRedirectCallback(url);
            } catch (error) {
              console.error('Error details:', {
                message: error.message,
                stack: error.stack,
                errorName: error.name,
              });
            }
          }
          await Browser.close();
        }
      });

      console.log('Deep link listener registered');
    };

I know I could just handle the error and prompt login again to the user, but seems a bit strange to me, as it means the user would need to randomly re-authenticate again, when I explicitely set the refresh token to be valid for 15 days.

Any ideas what could be?

Thanks in advance!

Versions:

@auth0/auth0-react@^2.8.0”
@auth0/auth0-spa-js@^2.7.0”

Hi @lealoureiro

Welcome to the Auth0 Community!

I am sorry about the delayed response to your inquiry!

Can you make the following change inside your vite config file and let me know if it works as expected?

 {
      domain: import.meta.env.VITE_AUTH0_DOMAIN,
      clientId: import.meta.env.VITE_AUTH0_CLIENT_ID_NATIVE,
      cache: createSecureAuthCache(),
      useRefreshTokens: true,
      useRefreshTokensFallback: false,
      authorizationParams: {
        audience: import.meta.env.VITE_AUTH0_AUDIENCE,
        scope: 'openid profile email offline_access',
        redirect_uri: import.meta.env.VITE_AUTH0_CALLBACK_URL
      }
 }

I believe that the issue may be caused by the fact that the scopes are getting ignored because they are not included in the authorization parameters of login requests and that your application might not be receiving the refresh tokens at all.

Let me know if this works or not!

Kind Regards,
Nik

Hi Nik,

Thank you for your message.

After posting, I notice the mistake indeed, and I tried with scope parameter in right place and I still have the problem…

I added some debug screen in the app to show which keys are there in the cache and I cache see the cache is not wiped out, I added a dummy key to test and is still there.

When the error is ocorring, there is a token with user key name, but the refresh token key is not there, which is the case when the app is working fine.

I’m planning to upgrade the auth0 react 2.9.0 that was released recently, that includes auth0-spa-js 2.9.0. (I’m in 2.7.0 now).

Seems like there is a fix related with refresh token on 2.8.0: Releases · auth0/auth0-spa-js · GitHub

Regards,
Leandro Loureiro

Same problem after upgrading to latest version of auth0-react and auth0-spa-js :frowning:

I have no idea what is happening… Feels to me something is wrong with way I Auth0 in React, but the code for config/login/logout seems quite straight foward

Hi again!

Thanks for the extra info, can you confirm that you:

  • Enable Refresh Tokens for your Application
  • Have the Refresh Token grant enabled for the Application
  • You have registered your API within the Auth0 Dashboard
  • You have enabled refresh token rotation under your API in the Auth0 Dashboard
  • You are passing in the offline_access scope and the right audience
  • For your application, do you use credentialsManager in any way? If yes, is the refresh token available when using getCredetianls()?
  • When you have initiated the Auth0 Provider/Client, did you add the useRefreshTokens= true?

Kind Regards,
Nik

I had this setting disabled for the API this Mobile application is calling:

Could that be the issue?
I was thinking I only add to change settings in Application itself and not in the API.

Hi again.

Yes, that might be the cause of the issue since native application usually require you to have refresh token rotation enabled which depends on the Allow Offline Access option.

Let me know if that fixes the issue that you are experiencing!

Kind Regards,
Nik