Skip to content

Firebase Token Refresh System

Overview

The Firebase Token Refresh system provides seamless token renewal for authenticated users when their Firebase ID tokens expire or become invalid. This prevents unnecessary logouts and maintains user sessions by automatically refreshing expired tokens in the background.

Architecture

The token refresh system consists of three main components:

  1. Error Detection (pages/_app.js) - Detects token expiration errors
  2. Token Refresh Page (pages/token-refresh.js) - Handles the refresh process
  3. Authentication API (pages/api/set-auth.js) - Validates and stores new tokens

How It Works

1. Primary Token Refresh Mechanisms

The token refresh system employs a multi-layered approach to ensure tokens are always valid:

1.1 Firebase Auth State Listener (Primary)

The primary mechanism uses firebase.auth().onIdTokenChanged() to automatically detect token changes:

Flow:

  1. Listener Setup: onIdTokenChanged listener is established on app initialization
  2. Automatic Detection: Firebase automatically triggers when token is refreshed or expires
  3. Interval Setup: Sets up 55-minute refresh interval for the authenticated user
  4. Token Refresh: Calls setAuth(false) to refresh token without forcing cache bypass
  5. Server Update: New token is sent to /api/set-auth to update server-side cookies
  6. Seamless Operation: User continues without interruption

1.2 Interval-Based Forced Refresh (Fallback)

A setInterval mechanism provides a safety net for missed token refreshes:

Configuration:

  • Interval: Every 55 minutes (3,300,000ms)
  • Force Refresh: Calls firebase.auth().currentUser.getIdToken(true) to bypass cache
  • Background Operation: Runs silently without affecting user experience
  • Server Sync: Updates server-side cookies via /api/set-auth
  • Concurrency Protection: Uses isSettingAuth flag to prevent multiple simultaneous auth calls

Purpose:

  • Ensures tokens are refreshed even if onIdTokenChanged fails
  • Prevents edge cases where automatic refresh might be missed
  • Provides redundancy for critical authentication flows
  • Runs at 55 minutes to stay well within the 1-hour token expiry window

2. Error Detection and Trigger (Last Resort)

When proactive refresh mechanisms fail, the error handler in _app.js detects specific error patterns as a last resort.

Trigger Conditions:

  • CODE_407 errors (from GraphQL resolver context creation failures)
  • Firebase ID token related errors (when error message contains “Firebase ID token”)
  • Only triggers in browser environment (typeof window !== 'undefined')
  • Prevents infinite loops by checking current pathname (!== '/token-refresh')

3. Token Refresh Process

The /token-refresh page handles the actual token refresh when redirected from error conditions:

Flow:

  1. Firebase Auth Check: Waits for Firebase to initialize and checks auth state
  2. Token Refresh: Calls firebase.auth().currentUser.getIdToken(true) to force refresh
  3. Server Update: Posts new token to /api/set-auth to update server-side cookies
  4. Redirect: Returns user to original page using the returnUrl parameter

Key Features:

Security:

  • Validates return URLs to prevent open redirect attacks
  • Only allows same-origin redirects
  • Falls back to home page for invalid URLs

Error Handling:

  • Maximum 3 retry attempts for failed refreshes
  • Specific handling for different Firebase auth error codes
  • Graceful degradation to sign-in page for unrecoverable errors

User Experience:

  • Loading state with spinner during refresh
  • Internationalized error messages
  • Retry functionality with attempt counter

Implementation Details

Token Refresh Method (setAuth)

The core token refresh functionality includes several safety measures:

Token Validation:

  • Checks if token is a valid string and not empty
  • Throws error for invalid or empty tokens

Error Handling:

  • Handles specific Firebase auth error codes that require sign-out:
    • auth/user-token-expired
    • auth/user-disabled
    • auth/user-not-found
    • auth/invalid-user-token
    • auth/too-many-requests
  • Automatically signs out user and redirects to sign-in page for these errors
  • Reports other errors to Sentry for monitoring

Concurrency Control:

  • Uses isSettingAuth flag to prevent multiple simultaneous refresh attempts
  • Ensures only one refresh operation runs at a time

Performance Optimizations

  • Static Generation: Uses getStaticProps for faster page loads
  • Client-Side Only: All refresh logic runs in browser

Security Considerations

XSS Protection

  • HTTP-only cookies prevent JavaScript access
  • Token validation on server side
  • Secure cookie attributes in production

CSRF Protection

  • SameSite cookie attribute
  • Origin validation for return URLs
  • Proper CORS configuration

Open Redirect Prevention

  • Return URL validation against same origin
  • Fallback to safe default routes
  • URL parsing error handling

Testing

Manual Testing

  1. Let token expire (wait 1 hour or manually invalidate)
  2. Navigate to any protected page
  3. Should redirect to refresh page and back
  4. Verify no data loss or session interruption

Error Testing

  1. Disconnect internet during refresh
  2. Invalidate Firebase user account
  3. Test with malicious return URLs
  4. Verify proper fallback behaviors
  5. Test specific Firebase auth error codes (user-token-expired, user-disabled, etc.)
  6. Verify concurrent refresh prevention (multiple simultaneous calls)
  7. Test CODE_407 and Firebase ID token error detection and redirect