Skip to content

Understanding Authentication Tokens with Firebase Auth

og

Note: This article is a personal study note from when I was researching Firebase Auth tokens. It’s more of a compilation of what I learned during the process rather than a comprehensive guide.

This guide covers the fundamentals of authentication tokens in OAuth 2.0 / OpenID Connect, with a focus on Firebase Auth’s unique characteristics.


ConceptDescriptionExamples
AuthenticationVerifying “who you are”Login process, password verification, SAML authentication
AuthorizationVerifying “what you can do”Admin permission check, group membership verification
  • Authentication comes first, then authorization. An unauthenticated user cannot be authorized.

TokenPurposeContentUsage LocationTypical Validity
ID TokenUser identificationUID, email, name, etc.Frontend5 min - 1 hour
Access TokenResource access authorizationScopes, permissionsBackend API5 min - 1 hour
Refresh TokenToken renewalRandom stringToken refresh requests7 - 90 days
  • Purpose: Prove “who this person is”
  • Usage: Consumed within the client (frontend)
  • Use cases: Display username, show profile, personalize UI
  • Format: JWT (JSON Web Token)
  • Purpose: Prove “authorization to access this resource”
  • Usage: Sent to backend API (Authorization: Bearer {token})
  • Use cases: API authorization, resource access control
  • Format: JWT or Opaque Token
  • Purpose: Obtain new ID Token / Access Token
  • Usage: Token refresh requests to the auth server
  • Use cases: Get new tokens when Access Token expires, without re-login
  • Format: Opaque Token (contents not visible)

ID Tokens and Access Tokens are often created in JWT format. A JWT consists of three parts:

eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJodHRwczovL...
├──────────── Header ────────────┤├────────── Payload ──────────...
{
"alg": "RS256",
"typ": "JWT"
}
  • alg: Signature algorithm
  • typ: Token type
{
"iss": "https://securetoken.google.com/my-project",
"sub": "XeOK4KBxyz123abc",
"aud": "my-project",
"iat": 1704067200,
"exp": 1704070800,
"email": "user@example.com",
"firebase": {
"sign_in_provider": "password"
}
}
ClaimMeaning
issIssuer
subSubject = User ID (UID)
audAudience = Project ID
iatIssued At (timestamp)
expExpiration (timestamp)
emailEmail address
  • Header and Payload are signed with a private key
  • If tampered with, the signature won’t match
  • Server-side verification via verifyIdToken() etc.

Upon successful authentication, ID Token / Access Token / Refresh Token are issued simultaneously in a single response:

{
"access_token": "eyJhbG...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "dGhpcyB...",
"id_token": "eyJhbGci..."
}
  • Auth server decides: The auth server (IdP) sets all token validity periods
  • For JWTs: The exp claim contains the expiration timestamp
  • Configurability: Varies by provider - some allow developer configuration, others don’t
Auth ServerClientAuth ServerClientNormal Refresh FlowAPI calls can continueUser doesn't need to re-loginPrompt user to re-loginalt[Refresh Token Valid][Refresh TokenExpired/Invalid]Refresh Token Rotation (Enhanced Security)RT-1 can never be used againPrevents attacks with leaked old tokensDetect Access Token expiration(check exp claim)1POST /oauth/tokengrant_type=refresh_tokenrefresh_token={Refresh Token}2Validate Refresh Token- Check expiration- Verify not revoked3200 OKnew access_tokennew id_token(new refresh_token)4Store new tokens5401 Unauthorizedinvalid_grant6Clear stored tokens7Request refresh with RT-18Invalidate RT-19new access_tokennew id_tokennew refresh_token=RT-210
  • When Access Token expires, use Refresh Token to get new tokens
  • A single Refresh Token refreshes both ID Token and Access Token
  • User doesn’t need to re-login

AspectStandard OAuth 2.0Firebase Auth
Token typesID Token + Access TokenID Token only
API authenticationUses Access TokenUses ID Token
Management complexityManage 2 typesManage 1 type
  • In Firebase, ID Token handles both “authentication” and “authorization”
  • Simpler for developers - only one token type to manage
ItemFirebase Auth
ID Token validity1 hour (fixed, not configurable)
Refresh Token validityVirtually unlimited

Firebase Refresh Token is invalidated only when:

  • User is deleted
  • User is disabled
  • Password or email address is changed
  • revokeRefreshTokens(uid) is explicitly called

Client-side SDK (Automatic Refresh Token Usage)

Section titled “Client-side SDK (Automatic Refresh Token Usage)”
SDKUse Case
Firebase JavaScript SDK (firebase/auth)Web apps
Firebase iOS SDKiOS apps
Firebase Android SDKAndroid apps
Firebase Flutter SDKCross-platform apps
  • Just call user.getIdToken() - automatically refreshes when expired
  • Developers don’t need to handle Refresh Token directly

Server-side Admin SDK (Refresh Token Management/Revocation)

Section titled “Server-side Admin SDK (Refresh Token Management/Revocation)”
SDKUse Case
Firebase Admin SDK for Node.jsServer-side
Firebase Admin SDK for PythonServer-side
Firebase Admin SDK for JavaServer-side
Firebase Admin SDK for GoServer-side
  • Cannot directly access Refresh Token
  • Can only invalidate via revokeRefreshTokens(uid)
PerspectiveWhat It Looks Like
User’s view”I’m always logged in”
Internal behaviorID Token expires every hour and gets renewed as needed

Why this design?

AspectReason
Short ID Token (1 hour)Even if leaked, becomes invalid after 1 hour (limits damage)
Long Refresh Token (virtually unlimited)Users don’t want to log in repeatedly (UX priority)
SDK auto-refreshSecurity and UX balance without developer effort

Firebase Auth achieves “security-wise tokens change every hour, but UX-wise the user stays logged in”.


Where to store tokens and session information involves security vs. convenience trade-offs.

StorageLifetimeCross-tabAuto-sent to ServerJS AccessCapacity
Session StorageUntil tab closesNoNoYes~5MB
Local StoragePersistent (until deleted)YesNoYes~5MB
CookieSet via maxAge/expiresYesYesBlocked by httpOnly~4KB
IndexedDBPersistentYesNoYesLarge
  • Storage location: Browser (Web Storage API)
  • Lifetime: Until tab/window closes
  • Scope: Same tab only (not shared across tabs)
  • Use cases: Temporary data (UI state needed only while logged in)
// Store
sessionStorage.setItem('key', 'value');
// Retrieve
const value = sessionStorage.getItem('key');
// Delete
sessionStorage.removeItem('key');
  • Storage location: Browser (Web Storage API)
  • Lifetime: Persistent until explicitly deleted
  • Scope: Shared within same origin (across all tabs)
  • Use cases: User settings, cache data
// Store
localStorage.setItem('key', 'value');
// Retrieve
const value = localStorage.getItem('key');
// Delete
localStorage.removeItem('key');
  • Storage location: Browser
  • Lifetime: Set via maxAge/expires
  • Scope: Same origin (subdomains configurable)
  • Key feature: Automatically sent to server with requests
  • Use cases: Auth tokens, session management
// Set (server-side)
response.cookies.set('name', 'value', {
httpOnly: true, // JS cannot access
secure: true, // HTTPS only
sameSite: 'lax', // CSRF protection
maxAge: 86400, // 1 day (seconds)
});
  • Storage location: Browser (NoSQL database)
  • Lifetime: Persistent until explicitly deleted
  • Scope: Within same origin
  • Use cases: Large data, offline data, Firebase SDK token storage

Firebase Auth SDK automatically stores ID Token and Refresh Token in IndexedDB:

IndexedDB: firebaseLocalStorageDb
└─ firebaseLocalStorage
└─ firebase:authUser:{apiKey}:{appName}
Storage LocationXSS Attack RiskRecommended Use
Session StorageCan be stolenTemporary data
Local StorageCan be stolenNon-sensitive data
Cookie (httpOnly)Cannot be read by JSAuth tokens (recommended)
IndexedDBCan be stolenFirebase SDK default
Memory onlyLost on page navigationMost secure but inconvenient

These have similar names but completely different mechanisms:

AspectSession StorageSession Cookie
ManagerBrowserServer issues → Browser stores
LifetimeUntil tab closesServer-defined (5 min - 14 days)
Server transmissionManualAutomatic
httpOnlyNot available (always JS accessible)Configurable
Use caseTemporary UI stateAuth session management

Cookies are classified into 2 types based on expiration settings:

TypeExpiration SettingDisappears WhenUse Case
Session CookieNone (maxAge/expires not set)Browser closesTemporary session info
Persistent CookieSet (maxAge/expires defined)Expiration reachedMaintain login state
Section titled “7.2 Firebase Auth’s “Session Cookie””

In Firebase Auth documentation, “Session Cookie” has a different meaning from the general definition:

AspectGeneral Session CookieFirebase Auth Session Cookie
DefinitionCookie without maxAge/expiresCookie issued by createSessionCookie()
LifetimeUntil browser closes5 min - 14 days (configurable)
Technical classificationSession CookiePersistent Cookie
Use caseTemporary sessionServer-side session management

In other words, Firebase’s “Session Cookie” is technically a Persistent Cookie.

Section titled “7.3 Session Cookie Overview (Firebase Context)”

Firebase Auth Session Cookie is a mechanism for managing user sessions server-side. Send ID Token to server, convert it to Session Cookie, then use Cookie-based auth for subsequent requests.

AspectID Token Direct UsageSession Cookie Usage
StorageClient (memory/IndexedDB)Server issues → Browser Cookie
Validity1 hour (fixed)5 min - 14 days (configurable)
RefreshClient SDK automaticServer-side explicit renewal
SecurityJavaScript accessiblehttpOnly blocks JavaScript access
Section titled “7.4 Creating Session Cookie (Firebase Admin SDK)”
// Server-side: Receive ID Token and issue Session Cookie
const expiresIn = 60 * 60 * 24 * 1000; // 1 day (milliseconds)
// Verify ID Token and create Session Cookie
const sessionCookie = await adminAuth.createSessionCookie(idToken, { expiresIn });
// Set Cookie in response
response.cookies.set('__session', sessionCookie, {
httpOnly: true, // JavaScript cannot access (XSS protection)
secure: true, // HTTPS only
sameSite: 'lax', // CSRF protection
maxAge: expiresIn / 1000,
path: '/',
});
// Server-side: Verify Session Cookie
const sessionCookie = cookies().get('__session')?.value;
const decodedClaims = await adminAuth.verifySessionCookie(sessionCookie, true);
// Access decodedClaims.uid, decodedClaims.email, etc.
FlagValueEffect
httpOnlytrueJavaScript cannot access (XSS protection)
securetrueCookie sent only over HTTPS
sameSitelaxOnly send Cookie for same-site requests (CSRF protection)
maxAgesecondsCookie validity period
path/Path where Cookie is valid
ItemValue
Minimum5 minutes (300,000 milliseconds)
Maximum14 days (1,209,600,000 milliseconds)
ConfigurationexpiresIn option in createSessionCookie()
Section titled “7.8 ID Token Direct Usage vs Session Cookie”
AspectID Token Direct UsageSession Cookie
Implementation complexitySimpleMore complex (Cookie management needed)
Validity flexibility1 hour fixed5 min - 14 days configurable
XSS resistanceLower (JS accessible)Higher (httpOnly)
CSRF resistanceHigher (explicit header required)Protected by sameSite
RefreshSDK automaticManual refresh process needed
SSR supportAdditional implementation neededCookie auto-sent

XSS Attack Resistance

MethodStorage LocationXSS Risk
Session CookieCookie (httpOnly)JS cannot read. However, Cookie is auto-sent, so attacker could potentially call APIs
ID Token Direct UsageIndexedDB / MemoryRisk of theft via localStorage.getItem() or IndexedDB. Can be obtained via Firebase SDK’s getIdToken()

CSRF Attack Resistance

MethodTransmissionCSRF Resistance
Session CookieAuto-sent (Cookie)sameSite: 'lax' limits to same-site requests. However, may be sent for GET requests
ID Token Direct UsageExplicit (Authorization: Bearer)Not auto-sent in CSRF, so higher resistance

Token Leakage Impact

MethodValidityMitigation
Session Cookie1 day (configurable)Invalidated after 1 day. Can call revokeRefreshTokens() server-side for immediate invalidation
ID Token Direct Usage1 hour (fixed)Invalidated after 1 hour. However, if Refresh Token leaks, long-term impact (Firebase is virtually unlimited)

Decision Points

AspectID Token Direct Usage Suits WhenSession Cookie Suits When
XSS risk toleranceInternal portal with low XSS riskWant to minimize XSS risk
CSRF risk toleranceWant complete CSRF preventionNo sensitive operations on GET requests
Implementation/maintenance costWant simplicityPrioritize security
Token leakage impact1-hour invalidation is acceptable1-day validity is acceptable

Reference Documentation:

Instead of using Session Cookie, send ID Token directly to API. Leverages Firebase SDK’s auto-refresh feature.

Characteristics:

ItemContent
Session CookieNot needed (not created)
Session refreshNot needed (SDK auto-manages)
API callsExplicitly send via Authorization: Bearer {idToken}
Server verificationUse verifyIdToken()

API Call Example:

// Client-side
const user = auth.currentUser;
const idToken = await user.getIdToken(); // Auto-refresh if near expiration
const response = await fetch('/api/data', {
headers: {
'Authorization': `Bearer ${idToken}`,
},
});

Server-side Verification:

// API Route
const authHeader = request.headers.get('Authorization');
const idToken = authHeader?.split('Bearer ')[1];
const decodedClaims = await adminAuth.verifyIdToken(idToken);
// Access decodedClaims.uid, decodedClaims.email, etc.

Changes When Migrating from Session Cookie:

Change LocationContent
/api/auth/session/route.tsDelete or disable
session-refresh.tsDelete (not needed)
Each API RouteverifySessionCookie()verifyIdToken()
Client API callsAdd Authorization: Bearer {idToken}

ProviderID TokenAccess TokenRefresh TokenConfigurable?
Firebase1 hourN/A (ID Token serves both)Virtually unlimitedNo
Google1 hour1 hour6 months (if unused)No
Microsoft1 hour1 hour90 days (default)Yes
Auth010 hours24 hours2 weeksYes
AWS Cognito1 hour1 hour1 day - 3650 daysYes
OktaConfigurableConfigurableConfigurableYes

TermDescription
AuthenticationIdentity verification
AuthorizationPermission verification
ID TokenIdentity certificate (JWT)
Access TokenResource access rights
Refresh TokenFor token renewal
Session CookieCookie for server-side session management
UIDUser ID
ClaimsAttributes within token
JWTJSON Web Token format
SignatureTamper prevention
IdPIdentity Provider (auth server)
Custom ClaimsDeveloper-added custom attributes
httpOnlyCookie flag preventing JavaScript access
secureCookie flag for HTTPS-only transmission
sameSiteCookie flag for CSRF protection
XSSCross-Site Scripting attack
CSRFCross-Site Request Forgery attack
Session StorageBrowser storage that clears on tab close
Local StoragePersistently stored browser storage
IndexedDBIn-browser NoSQL database
Persistent CookieCookie with set expiration (persists until expiry)