Files
Salon/docs/execplans/auth-phone-first-hardening.md
T

4.0 KiB

Phone-first Auth Hardening

This ExecPlan is a living document. It stays synchronized with docs/PLANS.md (see the "Queued Next Review Focus" section there) and tracks everything needed to bring the authentication API to a consolidated, phone-first contract with a pre-verified lifecycle and consistent display paths. The remaining work must be test-driven: one sub-flow defines specs/tests, another implements against those specs, and every commit must pass the relevant backend suite.

Purpose / Big Picture

Users must be able to log in via phone OTP without a password, the backend must keep phone numbers as the canonical identifier, and every surface that mentions a person should fall back to the phone number when email is absent. The new contract documents the public login/auth endpoints and ensures that the pre-verification lifecycle is deterministic, rate limits stay sensible, and audits display clear phone-first names. The deliverables include updated documentation, new guards/tests against regressions, and polished serializers/models that no longer assume user.email exists.

Milestones

  1. Spec & Test Subagent: Formalize and implement the missing specs around pre-verification, OTP purpose safety, rate-limit exposure, and display fallbacks. This milestone produces new pytest modules covering the pre-verification promise, the OTP contract (auth vs verify), and the fallback names used across staff, availability, and reviews. Success is measured by the new tests failing before implementation changes and passing afterward.
  2. Implementation Subagent: Update serializers, models, and docs to satisfy the specs. This includes reinforcing the user lifecycle (pre-verify), documenting the intended login surface (phone OTP as source-of-truth, register/token deprecated), tuning rate-limit metadata in responses, and ensuring every display path prefers phone numbers. Implementation is validated by rerunning the pytest suite (python3 -m pytest backend/apps/accounts/tests backend/apps/salons/tests).

Progress

  • (2026-03-14 12:00 UTC) Capture the auth gaps in a dedicated ExecPlan and outline the test-first flow for the missing invariants.
  • (2026-03-14 13:55 UTC) Added specs/tests for display-name fallbacks, phone auth 404 handling, and serializer coverage so the new contract fails before implementation.
  • (2026-03-14 14:30 UTC) Implemented User.display_name, updated serializers/models/admin, documented the canonical phone OTP surfaces, and confirmed the specs pass via python3 -m pytest backend/apps/accounts/tests backend/apps/salons/tests.

Surprises & Discoveries

  • Pytest reports jwt.api_jwt.InsecureKeyLengthWarning because the test signing key is 8 bytes long. Evidence: the two warnings emitted during python3 -m pytest backend/apps/accounts/tests backend/apps/salons/tests (see the console output).

Decision Log

  • (2026-03-14 12:00 UTC) Committed to the pre-verified user lifecycle: PhoneAuthRequestView creates the user (if missing) before sending an auth OTP, and PhoneAuthVerifyView marks is_phone_verified true immediately upon successful verification.
  • (2026-03-14 12:00 UTC) Deferred OAuth linking and non-KSA normalization until after the current auth reliability milestone, per the user request.
  • (2026-03-14 14:05 UTC) Added User.display_name so every read path has a phone-first fallback and reused it in serializers/models to keep staff/review/booking strings readable for phone-only accounts.
  • (2026-03-14 14:07 UTC) Reordered the Django admin list and add forms to highlight phone_number so admin workflows no longer depend on email-centric defaults.

Outcomes & Retrospective

  • Phone-first auth now pre-creates customers before OTP sends, marks them verified on /api/auth/phone/verify/, and treats passwords as deprecated. Serializers and models no longer fall back to user.email; they use User.display_name so phone-only accounts always show a meaningful label. Django admin and README/risks docs document the canonical login surface, and the targeted pytest bundle passes with the existing JWT warnings noted above.