Tutorials · 2026
2026 Firebase Auth Email Verification Testing: How to Test Local Links?
For local development and automated tests, use the Firebase Authentication Emulator to retrieve and process verification links without relying on a real inbox. This guide covers manual checks, OOB code handling, account-state assertions, and the separate real-delivery checks you need in staging.
Firebase’s JavaScript Auth API exposes email verification as a boolean, emailVerified (official JS Auth reference). Use that state as your local acceptance check: run local and automated tests against the Firebase Authentication Emulator, where you can retrieve the verification link without an external inbox. In staging, use a dedicated test inbox to confirm real delivery and link behavior. An emulator pass does not prove that production email delivery works.
Suitable: Firebase Auth developers, QA engineers, and indie developers testing registration, verification, or password recovery before release.
Not suitable: Anyone trying to prove real inbox delivery using only a local emulator.
Local emulator or staging inbox: choose by what you need to prove
The test environment determines what a successful result means. Choose the emulator when you need to validate your application’s verification behavior. Choose staging when you need to validate the real mail path as well.
| Test option | Where you get the link | What a pass confirms | What it does not confirm |
|---|---|---|---|
| Local Auth Emulator | Emulator output in the terminal, or its documented OOB-code endpoint | Your app can process the verification action and reflect the account state | Real email delivery, production mail configuration, or an external inbox’s behavior |
| CI with an Auth Emulator | The emulator used by the test run | The automated flow can retrieve and apply an action code, then assert account state | Delivery through your production email service |
| Staging with a dedicated test inbox | The email received by that address | The configured mail path, link destination, and post-verification application behavior | Delivery to every mailbox provider or acceptance by every test address |
The emulator’s email action links are available for local testing; you do not need a real mailbox just to exercise the simulated verification flow. Firebase documents this behavior in its Auth Emulator guide. That makes emulator testing useful for development and CI, but it draws a clear boundary: only a test that sends through the staging mail path can tell you whether that path reaches an inbox.
Local registration: where do you get the verification link?
The Firebase Auth Emulator prints email action links in the terminal when your app requests an email verification. The link is a test artifact from the emulator, not a message delivered to a real inbox. You can open it directly in a browser to exercise the local flow.
Keep the project configuration consistent. Your app must connect to the Auth Emulator, and your test must use the same project context as the emulator. If you accidentally point the app at a different Firebase project, a link generated in one environment may not represent the account state in the other. Check your emulator connection and project ID before treating a failed link as an application defect.
A link opening successfully is only the first check. The application should process the action and then show an updated account state. If your verification flow returns a user to a specific route, also check that the expected route and state are used. Firebase’s email action link guidance explains how to pass state through email actions; treat the configured continue URL as part of the behavior you test, not as decoration.
Step 1: Start the Auth Emulator and connect your app
Start the local Emulator Suite with Auth enabled. In your application’s development configuration, connect the Auth SDK to the emulator rather than the live service. Keep this choice explicit in environment-specific configuration so a local test cannot silently use a live Firebase project.
Before continuing, create or identify a test user in that emulator. Use a controlled test address that does not belong to a real person. That keeps test data separate from real accounts and avoids sending development messages to a colleague’s inbox.
Step 2: Create the user and trigger verification
Run your normal registration flow. Then request an email verification using the same application code path that production will use. The emulator should print a corresponding action link in its output.
If no link appears, check whether the app is connected to the Auth Emulator, whether the verification request completed, and whether you are inspecting the terminal for the emulator instance serving that app. Do not conclude that the mail system is broken: the emulator flow does not depend on a real external inbox.
Step 3: Open the link and return to the app
Copy the generated link from the emulator output and open it in a browser. Follow the application’s verification handling flow. Then return to the app and refresh or reload the user before checking the account state.
Treat the browser’s success message and the app’s updated user state as separate assertions. A page can display success without your test having confirmed that the expected user is now verified. Conversely, the account state may be correct while your custom page still has a broken success message or return link.
Step 4: Confirm the account state after the action
For a modular JavaScript app, reload the current user before reading emailVerified. For example:
import { reload } from "firebase/auth";
const user = auth.currentUser;
if (!user) {
throw new Error("No signed-in user to verify");
}
await reload(user);
if (!user.emailVerified) {
throw new Error("The account is not marked as verified");
}
reload refreshes the user data from Firebase, and emailVerified is the state your assertion should check. See the Firebase JS Auth API reference for the documented user properties and methods. If your test reads a cached user object without reloading it, it may report stale state and misdiagnose a successful action.
Automated tests: how do you retrieve and apply the OOB code?
For automation, the Auth Emulator has a dedicated endpoint for retrieving pending out-of-band (OOB) action codes. The documented route is:
GET http://localhost:9099/emulator/v1/projects/{project-id}/oobCodes
Use the project ID configured for the emulator. The response provides the pending action-code data, including the OOB code needed by the test. Check the Emulator guide for the emulator endpoint and the Firebase Auth REST reference when you need to verify Auth action request details. Keep this endpoint scoped to your test environment; it is an emulator facility, not a production mail-retrieval mechanism.
A reliable automated flow should connect each retrieved code to the user created by that test run. If a test suite shares one emulator among multiple test cases, avoid selecting the first pending code without checking its email or request type. A code for a different user or action can make a test appear intermittent even when the application is behaving consistently.
Step 5: Apply the action code in the test flow
Pass the retrieved code to the Firebase JS SDK action handler. For a verification action, the relevant method is applyActionCode:
import { applyActionCode } from "firebase/auth";
await applyActionCode(auth, oobCode);
After applying it, reload the test user and assert emailVerified. Firebase documents applyActionCode in its JS Auth API reference. Keep the assertions distinct: one checks that the action code was accepted; the other checks that the user state changed as expected.
If the operation fails, record a useful failure reason in the test output without logging sensitive values unnecessarily. Avoid dumping full action links or OOB codes into shared CI logs. Those links contain credentials for an account action and should be treated as temporary secrets, even in a test environment.
Step 6: Isolate and clean up test users
Use a predictable test-user naming strategy that does not collide across runs. Associate the OOB response with the account created by that run. Afterward, remove test accounts or reset the emulator state using the documented emulator cleanup mechanism.
Do not clear a shared emulator while other tests are still using it. In a local workflow, make cleanup explicit so you can reproduce failures before reset. In CI, isolate emulator state per job or test environment where possible. The objective is not to preserve test accounts indefinitely; it is to prevent leftover users and pending codes from contaminating later assertions.
Custom handler or default flow: what should your page test?
If your app uses a custom email action handler, test the handler itself rather than relying only on the emulator’s default page. Firebase’s custom email action handler guide describes the action-code handling model. Your page needs to read the action parameters, identify the requested operation, and invoke the corresponding Firebase Auth method.
For email verification, confirm that the handler obtains the OOB code and applies it with the verification action. Also check how your page responds when the code is missing, invalid, or no longer usable. Avoid presenting a success message before the action call succeeds. For failure cases, show a recoverable message and a next step, such as returning to the app or requesting a new verification email.
Check the continue link separately. Make sure it leads to the expected application route and does not drop required state. If your handler supports multiple email actions, test that it distinguishes verification from other actions rather than treating every link as the same operation. The Firebase guide is the implementation reference; your tests should assert your product’s own messages, routing, and recovery behavior.
Staging: how do you confirm that Firebase Auth mail really arrives?
An emulator link proves that your local action flow can be exercised. It does not prove that a configured sender, mail service, recipient domain, or production-style redirect works. For that, run a staging test through the real mail path with a dedicated test receiving address.
Use an address reserved for QA, not a personal account used for banking, work access, or account recovery. Keep test messages out of an employee’s primary inbox, and avoid using real personal information in test profiles. A separate address reduces test-data contamination and limits the chance that repeated verification attempts create unwanted mail for someone else.
For staging, check the whole path:
- Register a staging test user and request verification from the staging application.
- Confirm that the message reaches the dedicated test inbox. If it does not, inspect the sending and delivery path before changing the application’s success logic.
- Open the received link and check its destination. Verify that the action handler and any continue URL lead to the intended staging application.
- Complete verification, return to the app, and reload the user.
- Confirm the app now reflects the verified state and that the user can continue through the expected product flow.
A test inbox is an input to this check, not a universal guarantee. Some services may reject particular temporary addresses, and an address that accepts one message does not prove that every mailbox or future delivery will work. If you use a temporary address for staging, first confirm that it can receive the message and that you can access it for the duration of the test. The address lifespan guide can help you think through whether a temporary address fits a test that may need follow-up.
Define pass criteria by environment
Avoid one generic “email verification passed” result. Use criteria that match what each environment can actually prove.
Local development and CI: The test creates the intended emulator user, retrieves the matching action code or link, applies the action, and confirms the refreshed user is verified. It should also check any custom-handler success and error states that the application owns.
Staging: Add evidence that the message arrived in a dedicated inbox, the received link opens the expected environment, the application handles the action, and the refreshed account state reflects verification. If delivery fails, report a staging delivery failure rather than marking the emulator test as failed or claiming that local behavior was not tested.
Release decision: Do not use a local emulator pass as evidence of live delivery. Do not use a successful delivery to one test inbox as a promise that every recipient provider will accept future messages. Record which environment was tested and which part of the mail path the result covers.
If you need to check actual staging receipt without exposing your personal address, tmp10 can be one option for a temporary test inbox. It is designed for immediate use without registration, so you can keep a one-off test separate from your primary mailbox and reduce the chance of test or follow-up messages cluttering it. It is not a dependency for the Auth Emulator, and you should still verify that the address receives your particular staging message before relying on it. Start from the tmp10 inbox only for test accounts that do not need long-term recovery or ongoing correspondence.