Authentication
Firenook’s Auth emulator implements every operation the official Auth emulator does. Read this page to set it up, sign users in with test credentials, read the codes and links Auth would have sent, and see what it leaves out.
Set it up
Section titled “Set it up”Auth has no top-level section in firebase.json, so it starts when
emulators.auth is there:
{ "emulators": { "auth": { "port": 9099 } }}Point your app at it. In a web app the address includes http://:
import { getAuth, connectAuthEmulator } from 'firebase/auth';
const auth = getAuth(app);connectAuthEmulator(auth, 'http://127.0.0.1:9099');The Admin SDK reads FIREBASE_AUTH_EMULATOR_HOST=127.0.0.1:9099, which your
Functions and firenook emulators:exec get set for them.
What it emulates
Section titled “What it emulates”All 61 operations of the official Auth emulator, checked against a recorded run of it:
- Sign-in methods: email and password, anonymous, custom tokens, email links, phone numbers, and test sign-in with Google, Apple and other providers.
- Accounts: update and delete, session cookies, and the Admin SDK’s batch create, get, delete and query.
- Multi-factor and passkeys: SMS second factors, and passkeys.
- Tenants and project and tenant configuration.
- Blocking functions (
beforeUserCreatedandbeforeUserSignedIn) and theuser.createanduser.deletetriggers. - Export and import in the official format.
- The emulator’s own routes: the codes and links it issued, its configuration, and the popup and redirect helper pages.
It does not do what the official emulator answers with 501 either: identity
provider, SAML and OIDC configuration, initializeAuth, IAM on tenants, Game
Center, reCAPTCHA enforcement and TOTP second factors. It sends no real email
or SMS, and its ID tokens are unsigned, as the official emulator’s are, so
production token verification does not apply.
Sign a user up
Section titled “Sign a user up”The SDK calls are the ones you use in production. A sign-up, an email verification read back from the emulator, and a custom claim set through the Admin SDK:
const { user } = await createUserWithEmailAndPassword(auth, 'ada@example.com', 'secret-password');console.log('signed up', user.uid, user.email);await sendEmailVerification(user);
const res = await fetch('http://127.0.0.1:9099/emulator/v1/projects/demo-shop/oobCodes');const { oobCodes } = await res.json();console.log('oobCodes', oobCodes.map((c) => `${c.requestType} ${c.email}`));
await getAdminAuth().setCustomUserClaims(user.uid, { role: 'admin' });const token = await user.getIdTokenResult(true);console.log('claims', token.claims.role);$ node signup.mjssigned up Zy9R83B4S4AMfCNTfnAt5JwmeB3Z ada@example.comoobCodes [ 'VERIFY_EMAIL ada@example.com' ]claims adminCustom claims are held to the 1,000-character limit, counted as the official
emulator counts them: the compact JSON’s length in UTF-16 code units, so é
counts one and an emoji two.
Test sign-in with providers
Section titled “Test sign-in with providers”Sign in with Google, Apple or another provider by handing the SDK a credential made of plain JSON claims. Nothing reaches the provider:
const credential = GoogleAuthProvider.credential( JSON.stringify({ sub: 'g-123', email: 'grace@example.com', email_verified: true }),);const { user } = await signInWithCredential(auth, credential);console.log('google', user.email, user.providerData.map((p) => p.providerId));google grace@example.com [ 'google.com' ]signInWithPopup and signInWithRedirect open the emulator’s own helper
page, where you pick an account or add a new one.
Codes and links
Section titled “Codes and links”Every email link and SMS code Auth would have sent is printed in the log:
firenook auth: To verify the email address ada@example.com, follow this link: http://127.0.0.1:9099/emulator/action?mode=verifyEmail&lang=en&oobCode=SuGQzETq…&apiKey=fake-api-keyfirenook auth: To verify the phone number +15555550100, use the code 511430.The console’s Authentication page keeps them in an inbox, and the emulator’s REST routes return them to your tests. These routes exist only on the emulator:
| Route | What it does |
|---|---|
GET /emulator/v1/projects/{project}/oobCodes |
Email links: verification, password reset, email sign-in, email change |
GET /emulator/v1/projects/{project}/verificationCodes |
SMS codes for phone sign-in and SMS second factors |
GET and PATCH /emulator/v1/projects/{project}/config |
The emulator’s settings, such as signIn.allowDuplicateEmails |
DELETE /emulator/v1/projects/{project}/accounts |
Delete every account |
The codes and the account deletion also take a
/tenants/{tenant}/ form for a tenant. For example:
$ curl http://127.0.0.1:9099/emulator/v1/projects/demo-shop/verificationCodes{"verificationCodes":[{"code":"511430","phoneNumber":"+15555550100","sessionInfo":"4UQ4ly4D…"}]}The action links always carry the Auth emulator’s own address, including a code asked for from the console.
Blocking functions
Section titled “Blocking functions”A blocking function in your Functions codebase runs on every matching sign-up or sign-in, and what it throws reaches the client:
const { beforeUserCreated, HttpsError } = require('firebase-functions/identity');
exports.checkDomain = beforeUserCreated((event) => { if (event.data.email?.endsWith('@blocked.test')) { throw new HttpsError('permission-denied', 'Sign-ups from blocked.test are closed.'); } return { customClaims: { plan: 'free' } };});A sign-up from blocked.test then fails with the function’s message, and any
other sign-up gets the claim:
$ node blocking.mjsblocked: auth/internal-error Firebase: ((HTTP request to http://127.0.0.1:5001/demo-shop/us-central1/checkDomain returned HTTP error 403: {"error":{"message":"Sign-ups from blocked.test are closed.","status":"PERMISSION_DENIED"}})) (auth/internal-error).allowed, claims: freeYour users between runs
Section titled “Your users between runs”With --keep-data, accounts, codes and configuration are kept in
.firenook/state/auth-state.json and come back at the next start. The file is
written within 250 ms of a change, not before the response, and at once on a
clean stop; a tool that reads it while the suite runs can see a state that
much older. Keep data between runs has the details.
Like the official emulator’s, exported password hashes use a reversible
development format: keep real passwords and real user exports out of it.
- Run one service on its own: Auth without the rest of the suite.
- The console: users, their activity and the inbox.
- Security Rules: use these users in your rules tests.