Opt-in SMS two-factor auth (Twilio Verify) #739

Open
opened 2026-09-19 04:55:40 +00:00 by gambit-admin · 0 comments
Owner

Demoted from draft PR #173 (branch feature/sms-2fa, now closed) to keep it on the backlog rather than as a stale open PR. The draft was built against a very old main (migration 0020_user_phone, references to the retired startup healing DDL, route-count 289→293) so it needs a fresh rebuild — but the design below is sound and worth keeping.

Goal

Opt-in SMS second factor on password login across backend, web, and mobile. A user enables it in settings, registers a phone, and every subsequent password login then requires a texted 6-digit code. Nobody is affected until they opt in.

Design (from the draft)

  • Twilio Verify does the heavy lifting — generates, texts, expires, and rate-limits codes on Twilio's side. We store NO codes/expiries/attempt counters — only the user's phone number (User.phone).
  • Stateless challenge: password OK + mfa_enabled → server texts a code and returns a short-lived, typ-scoped challenge JWT (no server-side challenge table). Client completes at POST /auth/login/verify with challenge token + code → real access JWT.
  • Enrollment: /auth/mfa/enroll/start (store phone, text code) → /auth/mfa/enroll/verify (confirm + enable). Disable: /auth/mfa/disable.
  • Dormant until configured (safe): off until TWILIO_ACCOUNT_SID / TWILIO_AUTH_TOKEN / TWILIO_VERIFY_SERVICE_SID are set. Dev/design: unconfigured service accepts MFA_DEV_BYPASS_CODE; production-like APP_ENV fails CLOSED (503) instead of accepting the bypass.
  • SSO logins (/firebase, /apple) exempt — the provider already supplied a second factor. (Open question: gate them too?)

Surfaces

  • Backend: User.phone (+ a fresh migration on current head — NOT the old 0020), services/sms_2fa.py, challenge-token helpers, login gate + 4 routes.
  • Web: mfa view on login page; enroll/disable card in /settings.
  • Mobile: verify-2fa + security-2fa screens; AuthProvider two-step login + enroll helpers.

Known ceilings to resolve on rebuild

  • Phone requires explicit country code (no US +1 auto-prefix).
  • Disabling 2FA needs only a live session (no password/code re-auth) — consider requiring re-auth.
  • Rebuild must target current migration head and drop all references to the retired boot-time healing DDL.

Filed from a Claude Code session

Demoted from draft PR #173 (branch `feature/sms-2fa`, now closed) to keep it on the backlog rather than as a stale open PR. The draft was built against a very old `main` (migration `0020_user_phone`, references to the retired startup healing DDL, route-count 289→293) so it needs a fresh rebuild — but the design below is sound and worth keeping. ## Goal Opt-in SMS second factor on password login across backend, web, and mobile. A user enables it in settings, registers a phone, and every subsequent password login then requires a texted 6-digit code. Nobody is affected until they opt in. ## Design (from the draft) - **Twilio Verify does the heavy lifting** — generates, texts, expires, and rate-limits codes on Twilio's side. We store NO codes/expiries/attempt counters — only the user's phone number (`User.phone`). - **Stateless challenge:** password OK + `mfa_enabled` → server texts a code and returns a short-lived, `typ`-scoped challenge JWT (no server-side challenge table). Client completes at `POST /auth/login/verify` with challenge token + code → real access JWT. - **Enrollment:** `/auth/mfa/enroll/start` (store phone, text code) → `/auth/mfa/enroll/verify` (confirm + enable). Disable: `/auth/mfa/disable`. - **Dormant until configured (safe):** off until `TWILIO_ACCOUNT_SID` / `TWILIO_AUTH_TOKEN` / `TWILIO_VERIFY_SERVICE_SID` are set. Dev/design: unconfigured service accepts `MFA_DEV_BYPASS_CODE`; production-like `APP_ENV` fails CLOSED (503) instead of accepting the bypass. - **SSO logins (`/firebase`, `/apple`) exempt** — the provider already supplied a second factor. (Open question: gate them too?) ## Surfaces - Backend: `User.phone` (+ a fresh migration on current head — NOT the old 0020), `services/sms_2fa.py`, challenge-token helpers, login gate + 4 routes. - Web: `mfa` view on login page; enroll/disable card in `/settings`. - Mobile: `verify-2fa` + `security-2fa` screens; `AuthProvider` two-step login + enroll helpers. ## Known ceilings to resolve on rebuild - Phone requires explicit country code (no US `+1` auto-prefix). - Disabling 2FA needs only a live session (no password/code re-auth) — consider requiring re-auth. - Rebuild must target current migration head and drop all references to the retired boot-time healing DDL. --- Filed from a Claude Code session
gambit-admin added the featureapisecurity labels 2026-09-19 04:55:40 +00:00
Sign in to join this conversation.