How to maintain app page/location on browser refresh?

  • Which SDK this is regarding: @auth0/auth0-react
  • SDK Version: 1.9.0

I feel like I’m missing something very obvious, but I’m creating this topic because I haven’t been able to find anything useful in the Auth0 documentation, and all the issues and SO questions I’ve been able to find are about persisting login/authentication on browser refresh, rather than maintaining app location.

Problem: When the user is logged in and on a page with a dynamic url (eg /products/:productId), when the browser is refreshed, the url is changed to / and the user is back on the main menu.

What I think is happening
Based on console logs, HAR analysis, and inspection of the SDK code, here’s my best guess of what’s currently happening.

  1. User is on /product/1234. Auth0 client is setup with a redirectUri value of window.location.origin. The auth0.{clientId}.is.authenticated=true cookie is present.
  2. Browser refreshes
  3. A GET request is made to /product/1234 and page resources are downloaded
  4. Something in the Auth0 client triggers at GET request to <domain>.auth0.com/authorize.
  5. Then there’s a 302 redirect from <domain>.auth0.com/authorize
  6. Then there’s a GET to /. Presumably because redirectUri={window.location.origin}
  7. Then react-router does its thing and takes the user to the protected /menu route.

Other things to note

  • skipRedirectCallback: true didn’t make any difference
  • Even if there was a way to dynamically update the redirectUri, “Allowed Callback URLs” only allows wildcards for subdomains.

Questions

  • What is causing the auth request in step 4 above? (maybe the useEffect in auth0-provider.tsx?). Is there a way to pass appstate.returnTo into it? Or is there a way to set it to authenticate without redirecting at all?
  • Is there an easy way to maintain the app location? This feels like something Auth0 would have had a simple solution for already. Am I missing something? Or perhaps I’ve accidentally set my app up in a way the prevents it just working ‘out of the box’?

Hi @michael4,

Welcome to the Auth0 Community!

This has been implemented in our example app. Take a look:

Hi @dan.woda

Thanks for your welcome and for your help.

Yes, I had already spent some time trying to implement the ideas in that example app, but the problem I was having with that is that appState is always undefined, and window.location.pathname is /.

I’ll spend some time now trying to figure out how to make sure that appState.returnTo is set properly on browser refresh.

If I set appState.returnTo on the loginWithRedirect() function like this:

loginWithRedirect({
      appState: {
        returnTo: window.location.pathname,
      },
    });

that works fine when the user initially logs in. But I don’t know if that appState is maintained after the initial login, and even if it did, the returnTo value set on initial login is unlikely to be the same path the user is on when the browser refreshes.

So I guess my question above still stands: What is causing the auth request in step 4 above? (maybe the useEffect in auth0-provider.tsx ?). Is there a way to pass a appstate.returnTo value into it? Or is there a way to set it to authenticate without redirecting at all?

My code, as it currently stands:

export const AuthProvider: FC<PropsWithChildren<Props>> = ({ children }) => {
  const history = useHistory();

  const onRedirectCallback = (appState: AppState) => {
    history.push(
      appState && appState.returnTo
        ? appState.returnTo
        : window.location.pathname
    );
  };

  const auth0Options: Auth0ProviderOptions = {
    domain: process.env.REACT_APP_AUTH0_DOMAIN || '',
    clientId: process.env.REACT_APP_AUTH0_CLIENT_ID || '',
    redirectUri: window.location.origin,
    audience: process.env.REACT_APP_AUTH0_AUDIENCE || '',
    onRedirectCallback,
    cacheLocation: isCypress ? 'localstorage' : 'memory',
    useRefreshTokens: true,
  };

  return <Auth0Provider {...auth0Options}>{children}</Auth0Provider>;
};
const LoginPage = () => {
  const { isAuthenticated, isLoading, loginWithRedirect } = useAuth0();

  if (!isLoading && !isAuthenticated) {
    loginWithRedirect({
      appState: {
        returnTo: window.location.pathname,
      },
    });
  }

  return (
    <LandingPageLayout isLoading={isLoading}>
      <div />
    </LandingPageLayout>
  );
};