Skip to content

Understanding Authentication Tokens with Firebase Auth

Published:·Updated:

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

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

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"
}
{
  "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

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 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

AspectStandard OAuth 2.0Firebase Auth
Token typesID Token + Access TokenID Token only
API authenticationUses Access TokenUses ID Token
Management complexityManage 2 typesManage 1 type
ItemFirebase Auth
ID Token validity1 hour (fixed, not configurable)
Refresh Token validityVirtually unlimited

Firebase Refresh Token is invalidated only when:

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

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
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
// Store
sessionStorage.setItem('key', 'value');
// Retrieve
const value = sessionStorage.getItem('key');
// Delete
sessionStorage.removeItem('key');
// Store
localStorage.setItem('key', 'value');
// Retrieve
const value = localStorage.getItem('key');
// Delete
localStorage.removeItem('key');
// 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)
});

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)