QA Test Allowlists & Dev Overrides
Fixed OTP codes for demo and review accounts -how they work, and why they can't weaken production.
Last updated Thu Jul 16 2026 00:00:00 GMT+0000 (Coordinated Universal Time)
TL;DR -App reviewers and QA need to log in without waiting for a real text or email. The auth service allows this only for a tiny, explicitly-named list of phone numbers and emails that get a fixed code. Blanket "make every code the same" overrides are hard-blocked in production.
Who this is for -Product & business: read "The security stance" to understand the trade-off (each allowlisted account is a known-credential account). Backend, DevOps & QA: the env vars, exact behavior per list, dev-only defaults, and the operational how-to.
When Apple or Google reviews the app, or when QA runs through a flow, they need a working account that logs in instantly -they can't receive our SMS or email. The solution is a narrow allowlist: a short, explicit list of specific phone numbers and email addresses that accept a known fixed code instead of a random one. It's never a wildcard, and it never applies to real users.
In plain terms
- Only named accounts are affected. A phone number or email must be listed by hand. Everyone else always gets a random, hidden, one-time code.
- Listing an account means its login code is public. Anyone who knows an allowlisted email also knows its code. So each entry is effectively a shared demo account -keep the list tiny and prune it before launch.
- Production can't be globally weakened. The "make every code fixed" developer switches simply don't work in production. Only the per-identity allowlists do, and only for their named entries.
The security stance (read first)
Dev/test override flags (OTP_TEST_CODE, OTP_EXPOSE_CODE) are never
reachable in production -no flag can override that, because a
misconfigured flag would make every account's OTP a known constant. The two
deliberate exceptions are identity allowlists, scoped to specific
identifiers:
Anyone who knows an allowlisted identity also knows its code. Every entry is a known-credential account in production. Keep the lists tiny and prune them before public launch.
Email allowlist
These two env vars control the email allowlist:
| Env var | Effect |
|---|---|
OTP_TEST_EMAILS | Comma-separated emails (case-insensitive) with a fixed OTP |
OTP_TEST_EMAIL_CODE | The shared fixed code (default 1234) |
For allowlisted emails, in all environments:
- The OTP is the fixed code -for login/signup and email-change flows.
- No real email is sent (provider bypassed).
- The code is echoed back as
devCodein the response (it's already public knowledge). - The 30-second request cooldown is skipped (nothing to spam, speeds QA).
Everything else is untouched: non-listed emails get random codes, hidden in production, throttled normally.
Phone allowlist
The phone allowlist works the same way, with one env var and slightly different mechanics:
| Env var | Effect |
|---|---|
OTP_TEST_PHONE_NUMBERS | Comma-separated E.164 numbers |
Allowlisted phones skip Twilio delivery in production and have their (random) code exposed in the response -slightly different mechanics than email, same purpose.
Dev-environment defaults (non-production only)
These blanket conveniences exist only outside production -they're the switches that are hard-blocked in prod:
OTP_TEST_CODE-every OTP is this fixed code (default123456).devCodeis included in every OTP response.OTP_ALLOW_UNSENT=truelets requests succeed with no SMS/email provider configured.
Operational notes
How to actually manage an allowlist, and how it's kept honest:
- Preferred: manage the email allowlist with
scripts/otp-test-email.sh(list/add <email>/remove <email>) -it edits.env.production, restarts the backend with the correct--env-file, and verifies the fixed-code login end-to-end. - Manual fallback: changing an allowlist is an env-only deploy -edit
.env.production, thendocker compose ... up -d backend(no image rebuild). - The unit suite pins the guarantees: fixed code only for listed
identifiers, random+hidden for everyone else, empty list = disabled,
production ignores
OTP_TEST_CODE.
Related pages
- Authentication & Identity -the OTP flow these overrides sit on top of.
- Tokens, Sessions & Devices -what a successful login issues.
- Environments -where these env vars are set per deployment.