Identity Platform / Firebase Auth
floci-gcp emulates the Google Identity Toolkit API (identitytoolkit.googleapis.com v1)
wire-compatibly with the official Firebase Auth emulator: same paths, same unsigned
JWTs, same error strings. Anything that works against firebase emulators:start --only auth
is the compatibility target.
Configuration
| Variable | Default | Description |
|---|---|---|
FLOCI_GCP_SERVICES_FIREBASEAUTH_ENABLED |
true |
Enable/disable Firebase Auth |
Endpoint
Set FIREBASE_AUTH_EMULATOR_HOST=localhost:4588. Firebase SDKs then prefix requests with
the API hostname as a path, which floci-gcp serves directly on its single port:
| Method | Path |
|---|---|
POST |
/identitytoolkit.googleapis.com/v1/accounts:signUp | :signInWithPassword | :signInWithCustomToken | :lookup | :update | :delete (client, ?key= any non-empty API key) |
POST |
/identitytoolkit.googleapis.com/v1/projects/{project}/accounts[:lookup\|:update\|:delete\|:batchDelete] (admin, Authorization: Bearer owner) |
GET |
/identitytoolkit.googleapis.com/v1/projects/{project}/accounts:batchGet (admin listUsers) |
POST |
/identitytoolkit.googleapis.com/v1/projects/{project}:createSessionCookie (admin, Authorization: Bearer owner) |
POST |
/securetoken.googleapis.com/v1/token (refresh; JSON or form-urlencoded) |
DELETE |
/emulator/v1/projects/{project}/accounts (test helper: delete all users) |
Tokens
- ID tokens are unsigned JWTs (
alg: none, empty signature) with the claims the Admin SDK's emulator-mode verifier checks:iss=https://securetoken.google.com/{project},aud={project},sub/user_id,auth_time,firebase.{identities, sign_in_provider}. Custom claims (fromsetCustomUserClaimsor custom-tokenclaims) are merged into the payload. - Refresh tokens follow the emulator's base64-JSON record format;
POST /securetoken.../tokenwithgrant_type=refresh_tokenre-issues tokens. - Token revocation:
revokeRefreshTokenssetsvalidSince; tokens withiat < validSincefail withTOKEN_EXPIRED(wall-clockexpis intentionally not enforced, like the emulator). - Custom tokens: Admin SDK JWTs (signed or unsigned) and the emulator's strict-JSON form
(
{"uid": "...", "claims": {...}}) are both accepted. - Session cookies:
createSessionCookiere-issues a verified ID token's payload with a freshiat/expwindow andiss=https://session.firebase.google.com/{project}.validDurationis in seconds and must fall in[300, 1209600](5 minutes – 14 days), elseINVALID_DURATION. Like the official emulator, the value is first put through JavaScriptNumber()coercion and then|| MAX, so anything coercing toNaNor0(absent,0,false,"", or a string such as"3600s") silently falls back to the 14-day maximum, while anything coercing to another number (0.5,true,"0x10") reaches the range check and is rejected there. Radix prefixes ("0x1000"), exponent notation ("3.6e3") and ECMAScript whitespace (which includes NBSP) are read the wayNumber()reads them. There is no verification endpoint as the Admin SDKs verify session cookies locally, andcheckRevokedgoes throughaccounts:lookup.
Quick Start
// export FIREBASE_AUTH_EMULATOR_HOST=localhost:4588
FirebaseApp app = FirebaseApp.initializeApp(FirebaseOptions.builder()
.setProjectId("floci-local") // the emulator's default project
.setCredentials(new EmulatorCredentials())
.build());
FirebaseAuth auth = FirebaseAuth.getInstance(app);
auth.createUser(new UserRecord.CreateRequest()
.setUid("alice").setEmail("alice@example.com").setPassword("secret123"));
auth.setCustomUserClaims("alice", Map.of("role", "admin"));
String customToken = auth.createCustomToken("alice");
// client exchanges it at accounts:signInWithCustomToken, then:
FirebaseToken decoded = auth.verifyIdToken(idToken);
// server-rendered apps: swap the ID token for an HttpOnly session cookie
String cookie = auth.createSessionCookie(idToken,
SessionCookieOptions.builder().setExpiresIn(TimeUnit.HOURS.toMillis(1)).build());
FirebaseToken session = auth.verifySessionCookie(cookie, true);
# client sign-up (any non-empty API key)
curl -X POST 'http://localhost:4588/identitytoolkit.googleapis.com/v1/accounts:signUp?key=fake-api-key' \
-H 'Content-Type: application/json' \
-d '{"email":"alice@example.com","password":"secret123"}'
# refresh
curl -X POST 'http://localhost:4588/securetoken.googleapis.com/v1/token?key=fake-api-key' \
-d 'grant_type=refresh_token&refresh_token=<refreshToken>'
# session cookie (validDuration is seconds, not a duration string)
curl -X POST 'http://localhost:4588/identitytoolkit.googleapis.com/v1/projects/floci-local:createSessionCookie' \
-H 'Content-Type: application/json' -H 'Authorization: Bearer owner' \
-d '{"idToken":"<idToken>","validDuration":3600}'
Scope and deviations
- Client API-key calls resolve to the emulator's default project
(
floci-gcp.default-project-id), mirroring the official emulator's single-project model. - Phase 1 covers email/password, anonymous, and custom-token flows plus the admin user CRUD
surface (create/lookup/update/delete/batchGet/batchDelete). Not yet implemented: OOB codes
(email verification / password reset), email-link, IdP, phone, MFA, passkeys, tenants
(including the tenant-scoped
projects/{p}/tenants/{t}:createSessionCookie), and the legacy v3relyingpartypaths: these return 404 (the official emulator returns 501 for unimplemented operations). - Passwords are stored in the emulator's literal
fakeHash:salt=...:password=...format. This is a development fixture, not a security boundary, and is identical to the official emulator. securetokenresponses reportproject_idas the project number, the same number Resource Manager reports for the default project (the Firebase emulator hardcodes"12345").