Firebase Auth → Auth0: Lazy Migration

An existing product's users already have Firebase Authentication accounts. Rather than a bulk migration, Auth0 can be introduced gradually: the first time an existing Firebase user hits an Auth0-protected surface, their Firebase session is exchanged — via RFC 8693 Custom Token Exchange — for a first-class Auth0 identity, provisioned just-in-time, with no forced password reset. Unlike the /demo/ctepage's toy HMAC-signed partner token, the subject token here is a real, Google-issued RS256 Firebase ID token, genuinely verified against Google's live JWKS.

Architecture — Just-in-Time Migration via Custom Token Exchange

Real, Google-signed

Firebase Auth

Existing user base

Auth0 /oauth/token

RFC 8693 token exchange

Auth0 identity

Created just-in-time

1Sign in with a Firebase Authentication account — a real, Google-issued RS256 ID token, not a demo-signed stand-in.
2The token is exchanged at Auth0's /oauth/token endpoint via RFC 8693 Custom Token Exchange — no browser redirect, no Universal Login.
3A dedicated Auth0 Action verifies the signature against Google's live JWKS, requires a verified email, and maps identity by Firebase UID.
4On first exchange, an Auth0 identity is provisioned just-in-time — no bulk migration, no forced password reset.

What this isn't: this does not establish a cross-app Auth0 SSO browser session — it demonstrates token exchange with explicit identity mapping, shown via this suite's usual token viewer. Compare with Custom Token Exchange, which proves the same RFC 8693 mechanism with a toy HMAC-signed partner token instead of a real external IdP.

Sign in with Firebase Authentication

email

password

This authenticates directly against Firebase Authentication, not Auth0. The resulting ID token is a real, Google-issued RS256 JWT — it only reaches Auth0 at the exchange step below.

Security Implications

Strengths

  • Verifies a real third-party JWKS (Google's live Firebase signing keys), not a toy shared secret — this is the part every real Custom Token Exchange integration with an external IdP actually has to solve, which the simpler /demo/cte page explicitly simplifies away.
  • Explicit email_verified gating closes an account-takeover vector a naive implementation would be exposed to: Firebase allows unverified-email signup by default, so without this check an attacker could self-register as any target email and be mapped onto whatever Auth0 account already exists for it.
  • Identity is mapped by Firebase UID (sub, namespaced by project), never by email — email is display-only after the UID-based mapping has already succeeded.

Risks & Considerations

  • The Action has a genuine runtime dependency on Google's JWKS endpoint being reachable; a cold cache miss during an outage would fail every exchange. Caching reduces how often this matters but does not eliminate it.
  • Firebase ID tokens carry no jti and are valid for up to one hour — a captured token can be replayed against the exchange endpoint for the remainder of that window. This is an accepted, documented limitation of this demo, not something additionally hardened against here.
  • This demo shows token exchange with explicit identity mapping, displayed via the usual AT/ID token viewer — it does not establish, and should not be read as establishing, a cross-app Auth0 SSO browser session.

Hardening Checklist

  • JWKS responses are cached (jwks-rsa's built-in cache, not hand-rolled fetch logic) — Action containers are ephemeral, so a cold fetch on every invocation is the realistic default, not an edge case to optimize away later.
  • algorithms: ['RS256'] is pinned explicitly in verification, closing the classic alg-downgrade attack where a token's header is swapped to claim HS256.
  • aud and iss are checked against the exact Firebase project ID/issuer URL, not merely checked for presence.