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:
- Error Detection (
pages/_app.js) - Detects token expiration errors - Token Refresh Page (
pages/token-refresh.js) - Handles the refresh process - 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:
- Listener Setup:
onIdTokenChangedlistener is established on app initialization - Automatic Detection: Firebase automatically triggers when token is refreshed or expires
- Interval Setup: Sets up 55-minute refresh interval for the authenticated user
- Token Refresh: Calls
setAuth(false)to refresh token without forcing cache bypass - Server Update: New token is sent to
/api/set-authto update server-side cookies - 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
isSettingAuthflag to prevent multiple simultaneous auth calls
Purpose:
- Ensures tokens are refreshed even if
onIdTokenChangedfails - 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_407errors (from GraphQL resolver context creation failures)Firebase ID tokenrelated 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:
- Firebase Auth Check: Waits for Firebase to initialize and checks auth state
- Token Refresh: Calls
firebase.auth().currentUser.getIdToken(true)to force refresh - Server Update: Posts new token to
/api/set-authto update server-side cookies - Redirect: Returns user to original page using the
returnUrlparameter
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-expiredauth/user-disabledauth/user-not-foundauth/invalid-user-tokenauth/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
isSettingAuthflag to prevent multiple simultaneous refresh attempts - Ensures only one refresh operation runs at a time
Performance Optimizations
- Static Generation: Uses
getStaticPropsfor 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
- Let token expire (wait 1 hour or manually invalidate)
- Navigate to any protected page
- Should redirect to refresh page and back
- Verify no data loss or session interruption
Error Testing
- Disconnect internet during refresh
- Invalidate Firebase user account
- Test with malicious return URLs
- Verify proper fallback behaviors
- Test specific Firebase auth error codes (user-token-expired, user-disabled, etc.)
- Verify concurrent refresh prevention (multiple simultaneous calls)
- Test CODE_407 and Firebase ID token error detection and redirect