Auth0 Session Is Lost Intermittently When Navigating Between Pages on My Website

Hi @joeroot.pk80

You are experiencing intermittent authentication session loss in your Single Page Application (SPA) when navigating between internal pages without logging out. After successful initial login, the application sometimes behaves as though the user is unauthenticated when moving to another route, even though the Auth0 session should still be valid. A page refresh or new login session can temporarily resolve the issue, making it difficult to reproduce consistently.

This is a well-documented race condition in Auth0 SPAs caused by a timing mismatch between when your application checks authentication state and when the Auth0 SDK finishes restoring the existing session. The solution involves properly managing the SDK initialization lifecycle and using both isLoading and isAuthenticated states together.

[Root Cause]

Your application is checking the authentication state before the Auth0 SDK has finished restoring the existing session from the browser's Auth0 session cookie. This causes the following sequence:

  1. User navigates to a new route — The route component mounts
  2. Application checks isAuthenticated immediately — At this point, the Auth0 SDK is still initializing and has not yet restored the session
  3. isAuthenticated returns false — Because the SDK hasn't finished loading the session yet
  4. Application renders unauthenticated content — Even though a valid Auth0 session exists
  5. SDK finishes restoring the session — But the component has already rendered incorrectly
  6. Inconsistent behavior — Sometimes the SDK finishes fast enough; sometimes it doesn't

This is exacerbated by:

  • Client-side navigation without full page reloads — The Auth0Provider may not be properly re-initialized during route changes
  • Browser restrictions on third-party cookies — Newer browser security features (ITP, SameSite) make silent authentication less reliable
  • Auth0 Developer Keys — If you are using social login with Auth0's default developer credentials, sessions will not persist across routes
  • Missing refresh token configuration — SPAs without refresh token rotation are more vulnerable to session loss

[Solution]

Step 1: Ensure Auth0Provider wraps your entire application

Verify that your Auth0Provider is at the root level of your application and wraps all routes. It should NOT be recreated during navigation.

Example (React):


import { Auth0Provider } from '@auth0/auth0-react';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';

function App() {
  return (
    <Auth0Provider
      domain={process.env.REACT_APP_AUTH0_DOMAIN}
      clientId={process.env.REACT_APP_AUTH0_CLIENT_ID}
      authorizationParams={{
        redirect_uri: window.location.origin,
      }}
    >
      <Router>
        <Routes>
          <Route path="/" element={<Home />} />
          <Route path="/page2" element={<Page2 />} />
        </Routes>
      </Router>
    </Auth0Provider>
  );
}

export default App;

Step 2: Use both isLoading and isAuthenticated together

Never check isAuthenticated alone. Always wait for isLoading to be false before rendering protected content.

Example (React with useAuth0 hook):

import { useAuth0 } from '@auth0/auth0-react';

function ProtectedPage() {
  const { isAuthenticated, isLoading, user } = useAuth0();

  if (isLoading) {
    return <LoadingSpinner />;
  }

  if (!isAuthenticated) {
    return <LoginPrompt />;
  }

  return <ProtectedContent user={user} />;
}

Step 3: Create a route protection wrapper

Implement a reusable protected route component that properly handles the loading state:

import { useAuth0 } from '@auth0/auth0-react';
import { Navigate } from 'react-router-dom';

function ProtectedRoute({ component: Component }) {
  const { isAuthenticated, isLoading } = useAuth0();

  if (isLoading) {
    return <LoadingSpinner />;
  }

  return isAuthenticated ? <Component /> : <Navigate to="/login" />;
}
<Route path="/protected" element={<ProtectedRoute component={ProtectedPage} />} />

Step 4: Enable Refresh Token Rotation (recommended)

For more reliable session persistence, enable refresh token rotation in your Auth0 configuration:

Navigate to Auth0 Dashboard → Applications → Your Application → Settings.

Scroll to Advanced Settings → Grant Types.

Enable:

  • Refresh Token grant type
  • Offline Access scope in your authorization parameters

Update your Auth0Provider configuration:

<Auth0Provider
  domain={process.env.REACT_APP_AUTH0_DOMAIN}
  clientId={process.env.REACT_APP_AUTH0_CLIENT_ID}
  authorizationParams={{
    redirect_uri: window.location.origin,
    scope: 'openid profile email offline_access', // Add offline_access
  }}
  useRefreshTokens={true}  // Enable refresh tokens
  cacheLocation="localstorage"  // Store tokens in localStorage
>

Step 5: Use a Custom Domain (optional but recommended)

If you are using social login, Auth0's default domain may have issues with third-party cookie restrictions. A custom domain improves session reliability:

Navigate to Auth0 Dashboard → Branding → Custom Domains.

Follow the setup instructions to configure a custom domain for your tenant.

Update your Auth0Provider to use the custom domain:

<Auth0Provider
  domain="auth.yourdomain.com"  // Use custom domain instead of auth0.com
  clientId={process.env.REACT_APP_AUTH0_CLIENT_ID}
  // ... rest of config
>

Step 6: Verify you are not using Auth0 Developer Keys

If you are using social login (Google, GitHub, etc.), verify that you have configured your own credentials:

Navigate to Auth0 Dashboard → Connections → Social → [Your Provider].

Check that you are NOT using Auth0's default developer keys. If you see a message like "Using Auth0 Development Keys," you must configure your own credentials for the provider.

Developer keys do not persist sessions across routes.

Step 7: Debug the session restoration

To confirm the SDK is properly restoring sessions, add logging:

import { useAuth0 } from '@auth0/auth0-react';
import { useEffect } from 'react';

function DebugAuth() {
  const { isAuthenticated, isLoading, user } = useAuth0();

  useEffect(() => {
    console.log('Auth state:', {
      isLoading,
      isAuthenticated,
      user: user?.email || 'no user',
      timestamp: new Date().toISOString(),
    });
  }, [isLoading, isAuthenticated, user]);

  return null;
}

// Add this component inside your Auth0Provider

Open your browser console and navigate between pages. You should see:

  1. Initial mount: isLoading: true
  2. After SDK restores session: isLoading: false, isAuthenticated: true
  3. On route change: isLoading should briefly be true again, then false

If isLoading never becomes true during route changes, the SDK is not re-initializing properly.

Step 8: Check browser cookie settings

If the issue persists, verify that Auth0 session cookies are not being blocked:

  1. Open browser DevTools → Application → Cookies
  2. Look for cookies from your Auth0 domain (e.g., auth0.com or your custom domain)
  3. Verify the cookies have:
    • SameSite=None and Secure flags (for cross-site requests)
    • Or SameSite=Lax for same-site requests
  4. If no Auth0 cookies exist, third-party cookies may be blocked in your browser settings

I would recommend reviewing these resources regarding similar issues to the one that you are experiencing:

Kind Regards,
Nik