To fix Firebase Auth session persistence issues, use setPersistence to explicitly configure how the auth state is stored. Firebase supports three persistence modes: browserLocalPersistence (survives browser close, the default), browserSessionPersistence (cleared when the tab closes), and inMemoryPersistence (cleared on page refresh). Call setPersistence before any sign-in method. Common issues include sessions not surviving page refreshes in SSR frameworks, IndexedDB being blocked in private browsing, and not waiting for onAuthStateChanged before checking the user.
Fixing Firebase Auth Session Persistence Issues
Firebase Auth persists the user session in the browser by default using IndexedDB (or localStorage as a fallback). When sessions unexpectedly disappear on page refresh or browser restart, the root cause is usually a misconfigured persistence mode, an SSR framework accessing auth on the server, IndexedDB being blocked, or the app checking auth.currentUser before onAuthStateChanged fires. This tutorial covers each scenario with specific fixes.
Prerequisites
- A Firebase project with Authentication enabled
- Firebase JS SDK v9+ installed
- A sign-in flow that works but loses sessions unexpectedly
- Basic understanding of browser storage mechanisms
Step-by-step guide
Understand the three persistence modes
Understand the three persistence modes
Firebase Auth offers three persistence types. browserLocalPersistence (default) stores the session in IndexedDB and survives browser close/restart. browserSessionPersistence stores the session only for the current tab and clears when the tab is closed. inMemoryPersistence stores the session only in JavaScript memory and clears on any page navigation or refresh. Choose the mode that matches your security and UX requirements.
1import { getAuth, setPersistence, browserLocalPersistence, browserSessionPersistence, inMemoryPersistence } from 'firebase/auth';23const auth = getAuth();45// Default — survives browser close6await setPersistence(auth, browserLocalPersistence);78// Session only — clears when tab closes9await setPersistence(auth, browserSessionPersistence);1011// In-memory only — clears on page refresh12await setPersistence(auth, inMemoryPersistence);Expected result: The auth session is stored according to the selected persistence mode.
Fix sessions lost on page refresh by waiting for onAuthStateChanged
Fix sessions lost on page refresh by waiting for onAuthStateChanged
The most common persistence 'issue' is actually a timing problem. When the page loads, Firebase needs to read the stored session from IndexedDB, which is asynchronous. If your code checks auth.currentUser immediately, it will be null even though a valid session exists. Always use onAuthStateChanged to wait for the auth state to be determined before rendering auth-dependent UI.
1import { getAuth, onAuthStateChanged } from 'firebase/auth';23const auth = getAuth();45// WRONG: May be null during session restoration6console.log(auth.currentUser); // null!78// CORRECT: Wait for auth state to be determined9onAuthStateChanged(auth, (user) => {10 if (user) {11 console.log('Session restored:', user.uid);12 // Render authenticated UI13 } else {14 console.log('No session found');15 // Redirect to login16 }17});Expected result: The app waits for the auth session to be restored before making auth-dependent decisions, eliminating false 'no user' states.
Handle SSR frameworks that access auth on the server
Handle SSR frameworks that access auth on the server
In Next.js, Nuxt, and other SSR frameworks, Firebase Auth only works on the client side. If you import and initialize Firebase Auth in a server component or during server-side rendering, it will fail silently or throw errors because there is no browser storage on the server. Guard all Firebase Auth calls with a check for the browser environment.
1import { getAuth, onAuthStateChanged, User } from 'firebase/auth';2import { useEffect, useState } from 'react';34// This hook safely handles SSR — only runs auth logic in the browser5export function useFirebaseAuth() {6 const [user, setUser] = useState<User | null>(null);7 const [loading, setLoading] = useState(true);89 useEffect(() => {10 // useEffect only runs in the browser, never on the server11 const auth = getAuth();12 const unsubscribe = onAuthStateChanged(auth, (firebaseUser) => {13 setUser(firebaseUser);14 setLoading(false);15 });16 return unsubscribe;17 }, []);1819 return { user, loading };20}Expected result: Firebase Auth initializes only in the browser, avoiding SSR errors and correctly restoring sessions on the client.
Work around IndexedDB restrictions in private browsing
Work around IndexedDB restrictions in private browsing
Some browsers restrict or disable IndexedDB in private/incognito mode. When this happens, Firebase cannot persist the session and the user appears logged out on every page refresh. Detect this by catching errors during auth initialization and falling back to inMemoryPersistence, which still works but only keeps the session for the current page.
1import { getAuth, setPersistence, browserLocalPersistence, inMemoryPersistence, signInWithEmailAndPassword } from 'firebase/auth';23const auth = getAuth();45async function signIn(email: string, password: string) {6 try {7 // Try local persistence first8 await setPersistence(auth, browserLocalPersistence);9 } catch (error) {10 // IndexedDB blocked (private browsing) — fall back11 console.warn('Local persistence unavailable, using in-memory');12 await setPersistence(auth, inMemoryPersistence);13 }1415 return signInWithEmailAndPassword(auth, email, password);16}Expected result: Sign-in works in private browsing with a graceful fallback, and the user is informed about the persistence limitation.
Debug persistence issues with the browser developer tools
Debug persistence issues with the browser developer tools
When sessions are unexpectedly lost, inspect the browser's storage to understand what Firebase is storing. Open DevTools > Application tab > IndexedDB and look for the firebaseLocalStorageDb database. Inside it, the firebaseLocalStorage object store should contain an entry with the user's auth token. If this database is empty or missing after sign-in, persistence is not working correctly.
1// Diagnostic: check what persistence mode is active2import { getAuth } from 'firebase/auth';34const auth = getAuth();56// Log the current auth state for debugging7console.log('Auth config:', {8 currentUser: auth.currentUser?.uid || 'null',9 tenantId: auth.tenantId,10 name: auth.name11});1213// After sign-in, verify the session is persisted14onAuthStateChanged(auth, (user) => {15 if (user) {16 console.log('Session persisted. UID:', user.uid);17 console.log('Token expiry:', user.metadata.lastSignInTime);18 }19});Expected result: You can inspect IndexedDB to verify the Firebase auth session is being stored correctly, helping diagnose the root cause of persistence failures.
Complete working example
1import { initializeApp } from 'firebase/app';2import {3 getAuth,4 setPersistence,5 browserLocalPersistence,6 browserSessionPersistence,7 inMemoryPersistence,8 signInWithEmailAndPassword,9 onAuthStateChanged,10 User11} from 'firebase/auth';1213const app = initializeApp({14 // Your Firebase config15});16const auth = getAuth(app);1718// Set persistence with fallback for restricted environments19export async function configureAuth(20 mode: 'local' | 'session' | 'memory' = 'local'21): Promise<void> {22 const persistenceMap = {23 local: browserLocalPersistence,24 session: browserSessionPersistence,25 memory: inMemoryPersistence,26 };2728 try {29 await setPersistence(auth, persistenceMap[mode]);30 } catch {31 console.warn(`${mode} persistence failed, falling back to memory`);32 await setPersistence(auth, inMemoryPersistence);33 }34}3536// Sign in with explicit persistence37export async function signIn(email: string, password: string) {38 await configureAuth('local');39 return signInWithEmailAndPassword(auth, email, password);40}4142// Wait for auth state (handles session restoration)43export function waitForAuth(): Promise<User | null> {44 return new Promise((resolve) => {45 const unsubscribe = onAuthStateChanged(auth, (user) => {46 unsubscribe();47 resolve(user);48 });49 });50}5152// React hook for SSR-safe auth state53export function useFirebaseAuth() {54 const [user, setUser] = useState<User | null>(null);55 const [loading, setLoading] = useState(true);5657 useEffect(() => {58 const unsubscribe = onAuthStateChanged(auth, (u) => {59 setUser(u);60 setLoading(false);61 });62 return unsubscribe;63 }, []);6465 return { user, loading };66}Common mistakes when fixing Firebase Auth Session Persistence Issues
Why it's a problem: Calling setPersistence after signIn instead of before
How to avoid: setPersistence must be called before any sign-in method. The persistence mode is applied to the next sign-in operation, not retroactively to an existing session.
Why it's a problem: Checking auth.currentUser on page load and assuming null means no session
How to avoid: auth.currentUser is null until Firebase finishes restoring the session from storage. Always use onAuthStateChanged to wait for the auth state to be determined.
Why it's a problem: Initializing Firebase Auth in a Next.js server component or getServerSideProps
How to avoid: Firebase Auth client SDK only works in the browser. Use useEffect or dynamic imports with ssr: false. For server-side auth, use the Firebase Admin SDK to verify tokens.
Why it's a problem: Not handling IndexedDB failures in private browsing mode
How to avoid: Wrap setPersistence in a try-catch and fall back to inMemoryPersistence. Inform the user that sessions will not persist across page refreshes in this mode.
Best practices
- Always call setPersistence before sign-in methods, not after
- Use onAuthStateChanged instead of auth.currentUser for initial auth state checks
- Implement a fallback to inMemoryPersistence for environments where IndexedDB is blocked
- Keep Firebase Auth initialization client-side only in SSR frameworks
- Use browserSessionPersistence for sensitive applications where sessions should not survive tab close
- Show a loading state while waiting for onAuthStateChanged to fire on page load
- For SSR auth verification, use Firebase Admin SDK with cookie-based tokens on the server
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
Explain Firebase Auth session persistence options and how to fix sessions being lost on page refresh. Show how to use setPersistence with browserLocalPersistence, browserSessionPersistence, and inMemoryPersistence. Include a React hook that handles the loading state with onAuthStateChanged and a fallback for private browsing mode.
Create a Firebase Auth persistence utility with configureAuth function supporting all three modes with automatic fallback, a signIn function that sets persistence before authentication, a waitForAuth promise-based helper, and a React useFirebaseAuth hook that is SSR-safe. Use modular SDK v9+ and TypeScript.
Frequently asked questions
Why does my user appear logged out after refreshing the page?
Most likely your code checks auth.currentUser before Firebase restores the session from IndexedDB. Use onAuthStateChanged to wait for the auth state to be determined. The currentUser property is null until restoration completes.
What is the default persistence mode in Firebase Auth?
The default is browserLocalPersistence, which stores the session in IndexedDB and survives browser close and restart. You only need to call setPersistence if you want a different mode.
Does Firebase Auth work in incognito mode?
Yes, but some browsers restrict IndexedDB in incognito mode. Firebase may fall back to localStorage or fail silently. Wrap setPersistence in a try-catch and fall back to inMemoryPersistence if local persistence fails.
How long does a Firebase Auth session last?
Firebase Auth sessions do not expire by default. The ID token refreshes automatically every hour. The session persists until the user explicitly signs out or you call auth.signOut(). Sessions can also be revoked server-side with the Admin SDK.
Can I use cookies instead of IndexedDB for Firebase Auth?
Firebase Auth does not natively use cookies. For cookie-based sessions (useful for SSR), create a session cookie using the Firebase Admin SDK's createSessionCookie method and manage it in your server middleware.
Can RapidDev help implement robust auth persistence across web and mobile?
Yes, RapidDev can configure Firebase Auth with the right persistence mode for your platform, implement SSR-compatible session management, and build fallback handling for restricted browser environments.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation