OTP phone sign-in flow β
This document explains how the phone-number + OTP sign-in works endβtoβend (backend + frontend) and how to extend or debug it, β
High-level overview β
- Goal: allow a user to sign in using only their phone number and a oneβtime code (OTP) sent via Firebase Phone Authentication.
- Where: mobile app. The web SignIn page is email/password only.
- Key constraints:
- OTP send goes straight to Firebase. We do not ask the backend whether the number is registered first (that 200-vs-404 check was an account-enumeration oracle; see auth brute-force protection).
- After a correct OTP, the backend still requires a Mongo user (
GET /auth/me/handleUserLogin). An unknown Firebase UID isUSER_NOT_REGISTEREDβ that is after the caller proved they have the phone. - We still reuse the existing Firebase session cookie mechanism (
POST /auth/session-login) for serverβside authentication on web; mobile uses the ID token on API calls.
Firebase is the only provider involved here for OTP delivery (no Twilio). SMS are billed through the Firebase / Google Cloud project associated with this environment.
End-to-end flow (diagram) β
At a glance:
- Firebase sends and verifies the SMS code. Send-OTP does not tell the client whether the number is already a Tellia user.
- Backend turns a verified Firebase user into an app session only if a Mongo user already exists for that UID.
Backend responsibilities β
Backend pieces live under src/firebase/* in the NestJS app.
1. No public phone-existence check β
POST /auth/check-phone is gone. Do not add it back: a 200 vs 404 (or any other distinguishable response) for "is this number registered?" is an enumeration oracle on a phone-number identity model.
OTP send lives in apps/mobile/auth/phone-auth.ts (signInWithPhoneNumber only).
2. Firebase lookup and user sync β
Key services:
FirebaseService.getUserByPhoneNumberinsrc/firebase/firebase.service.ts:- Thin wrapper around
admin.auth().getUserByPhoneNumberwith logging. - Admin / migrations only β not a public login probe.
- Thin wrapper around
FirebaseUserService.handleUserLogininsrc/firebase/firebase-user.service.ts:- Called after a successful OTP when the client hits
GET /auth/me(mobile) orPOST /auth/session-login(web). - Fetches the Firebase user by UID.
- If a Mongo user exists for that UID, updates identities/metadata.
- If not, does not auto-create (ENG-269). Throws
USER_NOT_REGISTEREDso the mobile client can delete the orphan Firebase user in production and send the person to web signup.
- Called after a successful OTP when the client hits
3. Session cookie issuance: POST /auth/session-login β
- Controller:
AuthController.sessionLogininsrc/firebase/auth.controller.ts. - Input:
{ idToken: string }from the Firebase client SDK. - Web email/password uses this. Mobile OTP uses the ID token on API calls instead.
Client responsibilities β
OTP UI lives in apps/mobile:
app/(auth)/phone-login.tsx+auth/phone-auth.tsβ send OTP via Firebase only.app/(auth)/verify-otp.tsx+auth/auth-context.tsxconfirmPhoneOtpβ confirm code, thenGET /auth/me.
Do not add a backend "is this number registered?" call on the send-OTP path.
Infrastructure, configuration & billing β
Provider choice β
- We use Firebase Phone Authentication for OTP delivery and verification.
- SMS costs are billed through the Firebase / Google Cloud project associated with the running environment.
Firebase configuration β
To enable and operate phone signβin, make sure the following are set up in the Firebase console:
- In Firebase Console β Auth β Signβin method:
- Enable Phone as a signβin provider.
- Configure any required reCAPTCHA / safety settings for web clients.
- In Firebase Console β Project settings:
- Ensure the web app credentials (
apiKey,authDomain, etc.) match the values used in the frontend.env(VITE_FIREBASE_*variables).
- Ensure the web app credentials (
If phone signβin is disabled or misconfigured, signInWithPhoneNumber will fail before any backend logic is called.
Billing & quotas β
Firebase charges per SMS for phone authentication. For details:
- See Firebase pricing (Authentication & Phone Auth section).
- Monitor usage & errors under Firebase Auth β Usage and in Cloud Logging for the project.
- Per-IP SMS and verification quotas, plus lockout recovery: Auth brute-force protection.
Operationally, if OTP suddenly fails in production, check in order:
- Firebase Phone Auth is enabled and not rateβlimited or blocked (
auth/too-many-requests/auth/quota-exceeded). - Project billing is active and has not hit SMS quota / spending limits.
- The numberβs owner has a Mongo user β otherwise OTP succeeds at Firebase and
/auth/mereturnsUSER_NOT_REGISTERED.
Links & references β
Use these as jumpingβoff points; the exact URLs may vary by project/environment.
- Firebase console (Auth β Phone signβin): https://console.firebase.google.com
- Firebase Auth phone sign-in docs: https://firebase.google.com/docs/auth/web/phone-auth
- Firebase pricing (Authentication / SMS): https://firebase.google.com/pricing
- Brute-force / enumeration control: Auth brute-force protection
- Internal credentials: search in 1Password for βFirebase β Maison Bleueβ to find the console login for the relevant project.