javascriptreactjsauth0auth0-lock

Auth0 does not persist login on page refresh for email/password


I am using Auth0 as my authentication provider for a SPA using React. I have followed the Auth0 react tutorial and this more detailed tutorial from their blog.

I am currently just using just email/password authentication. And the authentication works as expected for login/logout, retrieving user info etc.

However, when I refresh the page, the isAuthenticated value from the useAuth0 always returns false. Even after isLoading resolves to true, hence I have to log in again.

This behaviour strangely does not occur on Chrome or Firefox. It fails on Brave and Safari.

I noticed in a forum post on Auth0 (another person with a similar problem) that the Auth0Provider should be doing a authorize call using prompte=none, and it is. It also returns a successful 202 shortly after the page loads (but doesn't change isAuthenticated to true). This call also sets a cookie auth0.is.authenticated=true.

authorize?client_id=VALUE&redirect_uri=VALUE&scope=openid%20profile%20email&response_type=code&response_mode=web_message&state=VALUE&nonce=VALUE&code_challenge=VALUE&code_challenge_method=S256&prompt=none&auth0Client=VALUE

Here is my route that checks the auth state. This component is wrapped in the Auth0ProviderWithHistory code as suggested in the Auth0 tutorials.

export default function Routes() {
  const { isLoading, isAuthenticated } = useAuth0()

  const getDashboard = () => {
    //determine which dashboard to return
  }

  if (isLoading) return <p>Loading...</p>

  if (isAuthenticated) {
    return (
      <Suspense fallback={<p>loading...</p>}>
        <Switch>
          <Route exact path="/">
            {getDashboard()}
          </Route>
          <Route path="/customer/:customerId">
            <Customer />
          </Route>
          <Route>
            <NotFound />
          </Route>
        </Switch>
      </Suspense>
    )
  }

  return <SignInAndRegister />
}

I have noticed when I reload the page, and call the loginWithRedirect function I am not redirected to the Universal Login page, instead there are two token calls (POST and OPTIONS). The POST call response has the following details, should I somehow capture this and saving them to reuse them to login?

access_token: "VALUE"
expires_in: 86400
id_token: "VALUE"
scope: "openid profile email"
token_type: "Bearer"

As an experiment, I downloaded the react sample on the "Quick Start" section of an application in the Auth0 dashboard to see if the behaviour was replicated there. And it was.

I had the impression that the Auth0Provider should be handling the silent authentication automagically, is this not the case?

There are not many options to use with auth0-react npm package, so I am not sure what to try next. The only available functions are:

getAccessTokenSilently: ƒ (opts)
getAccessTokenWithPopup: ƒ (opts)
getIdTokenClaims: ƒ (opts)
isAuthenticated: false
isLoading: true
loginWithPopup: ƒ (opts)
loginWithRedirect: ƒ (opts)

If this isn't possible, it looks like I might have to migrate to the @auth0/auth0-spa-js SDK.


Solution

  • The issue was that Brave and Safari both use Intelligent Tracking Prevention (ITP), which was preventing the silent authentication from working.

    The solution that worked for me was to enable rotating refresh tokens (via the Auth0 dashboard) and providing additional props to the Auth0 provider.

    The two new props to add are: useRefreshTokens={true} and cacheLocation="localstorage".

    <Auth0Provider
      domain={process.env.REACT_APP_AUTH0_DOMAIN}
      clientId={process.env.REACT_APP_AUTH0_CLIENT_ID}
      redirectUri={window.location.origin}
      onRedirectCallback={onRedirectCallback}
      useRefreshTokens={true}
      cacheLocation="localstorage"
    >
      {children}
    </Auth0Provider>
    

    Here are the official docs to learn more about rotating refresh tokens: https://auth0.com/docs/tokens/refresh-tokens