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”