Skip to main content
RapidDev - Software Development Agency
firebase-tutorial

How to Fix Firebase Auth Session Persistence Issues

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.

What you'll learn

  • How to configure auth persistence with setPersistence and the three available modes
  • How to diagnose why sessions are lost on page refresh or browser restart
  • How to handle auth persistence in SSR frameworks like Next.js
  • How to work around IndexedDB restrictions in private browsing modes
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Intermediate7 min read10-15 minFirebase JS SDK v9+, Firebase Auth (all plans)March 2026RapidDev Engineering Team
TL;DR

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

1

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.

typescript
1import { getAuth, setPersistence, browserLocalPersistence, browserSessionPersistence, inMemoryPersistence } from 'firebase/auth';
2
3const auth = getAuth();
4
5// Default — survives browser close
6await setPersistence(auth, browserLocalPersistence);
7
8// Session only — clears when tab closes
9await setPersistence(auth, browserSessionPersistence);
10
11// In-memory only — clears on page refresh
12await setPersistence(auth, inMemoryPersistence);

Expected result: The auth session is stored according to the selected persistence mode.

2

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.

typescript
1import { getAuth, onAuthStateChanged } from 'firebase/auth';
2
3const auth = getAuth();
4
5// WRONG: May be null during session restoration
6console.log(auth.currentUser); // null!
7
8// CORRECT: Wait for auth state to be determined
9onAuthStateChanged(auth, (user) => {
10 if (user) {
11 console.log('Session restored:', user.uid);
12 // Render authenticated UI
13 } else {
14 console.log('No session found');
15 // Redirect to login
16 }
17});

Expected result: The app waits for the auth session to be restored before making auth-dependent decisions, eliminating false 'no user' states.

3

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.

typescript
1import { getAuth, onAuthStateChanged, User } from 'firebase/auth';
2import { useEffect, useState } from 'react';
3
4// This hook safely handles SSR — only runs auth logic in the browser
5export function useFirebaseAuth() {
6 const [user, setUser] = useState<User | null>(null);
7 const [loading, setLoading] = useState(true);
8
9 useEffect(() => {
10 // useEffect only runs in the browser, never on the server
11 const auth = getAuth();
12 const unsubscribe = onAuthStateChanged(auth, (firebaseUser) => {
13 setUser(firebaseUser);
14 setLoading(false);
15 });
16 return unsubscribe;
17 }, []);
18
19 return { user, loading };
20}

Expected result: Firebase Auth initializes only in the browser, avoiding SSR errors and correctly restoring sessions on the client.

4

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.

typescript
1import { getAuth, setPersistence, browserLocalPersistence, inMemoryPersistence, signInWithEmailAndPassword } from 'firebase/auth';
2
3const auth = getAuth();
4
5async function signIn(email: string, password: string) {
6 try {
7 // Try local persistence first
8 await setPersistence(auth, browserLocalPersistence);
9 } catch (error) {
10 // IndexedDB blocked (private browsing) — fall back
11 console.warn('Local persistence unavailable, using in-memory');
12 await setPersistence(auth, inMemoryPersistence);
13 }
14
15 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.

5

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.

typescript
1// Diagnostic: check what persistence mode is active
2import { getAuth } from 'firebase/auth';
3
4const auth = getAuth();
5
6// Log the current auth state for debugging
7console.log('Auth config:', {
8 currentUser: auth.currentUser?.uid || 'null',
9 tenantId: auth.tenantId,
10 name: auth.name
11});
12
13// After sign-in, verify the session is persisted
14onAuthStateChanged(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

auth-persistence.ts
1import { initializeApp } from 'firebase/app';
2import {
3 getAuth,
4 setPersistence,
5 browserLocalPersistence,
6 browserSessionPersistence,
7 inMemoryPersistence,
8 signInWithEmailAndPassword,
9 onAuthStateChanged,
10 User
11} from 'firebase/auth';
12
13const app = initializeApp({
14 // Your Firebase config
15});
16const auth = getAuth(app);
17
18// Set persistence with fallback for restricted environments
19export 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 };
27
28 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}
35
36// Sign in with explicit persistence
37export async function signIn(email: string, password: string) {
38 await configureAuth('local');
39 return signInWithEmailAndPassword(auth, email, password);
40}
41
42// 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}
51
52// React hook for SSR-safe auth state
53export function useFirebaseAuth() {
54 const [user, setUser] = useState<User | null>(null);
55 const [loading, setLoading] = useState(true);
56
57 useEffect(() => {
58 const unsubscribe = onAuthStateChanged(auth, (u) => {
59 setUser(u);
60 setLoading(false);
61 });
62 return unsubscribe;
63 }, []);
64
65 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.

ChatGPT Prompt

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.

Firebase Prompt

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.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.