3.5 KiB
After backend-phase-2 — auth is live over REST
The marketplace has its front door: phone-OTP login, revocable sessions with refresh-token
rotation + stolen-token detection, /me, and public role selection — all over REST, wrapping the
pre-existing JWE/TOTP/RBAC engine (nothing was rebuilt). Contract:
dev/contracts/domains/identity-auth.md; machine
schema: dev/contracts/openapi/swagger.v1.json (refreshed — 22 paths).
What the frontend (f1-b2) can now build
- Login flow:
POST api/v1/auth/request_otp→POST api/v1/auth/verify_otp(camelCase bodies; exact snake_case paths per the contract — noterequest_otp, nototp/request). - Session handling: store the token pair;
POST api/v1/auth/refreshrotates it (never reuse an old refresh token — replay = 401 + logout-everywhere);POST api/v1/auth/logout(send{}). AuthContextroles + role router:GET api/v1/mereturns masked phone,roles[], profile-completion flags (false until b3) andnurseVerificationStatus(not_starteduntil b6). Fresh users haveroles: []→ route toPOST api/v1/me/select_role(customer/nurse, both allowed, 403 for anything else). Refresh tokens after role selection — role claims are baked into the access token.- Errors: 400 invalid phone/code (safe, non-enumerating message), 401 with the standard envelope (also written by the auth stack itself), 403 admin self-assign, 429 over the OTP/auth per-IP limits. The envelope is unchanged (camelCase body, snake_case URLs).
What's mocked
- SMS delivery (
ISmsSender→ 🟡). The OTP code is written to the server log instead of a SIM. Local testing: callrequest_otp, read the code from the API console log,verify_otpwith it.
Rules baked into the API (don't fight them client-side)
- Phone is the only public credential; email is optional and never a login key.
- One resend per
auth_otp_resend_seconds(response saysotpSent: false+ wait time). - After
auth_otp_max_attemptswrong codes, verification refuses until a fresh OTP is requested. - Logout rotates the security stamp: all devices' access tokens die; they recover via refresh.
Schema / migration
Migration 20260701222425_IdentitySessionsAndUserExtensions applied to the dev DB on top of
b1's baseline: usr.Users gains Gender, NationalId (enc, NULL until b6 KYC),
NationalIdVerifiedAt, ShahkarVerifiedAt (auto-reset on phone change), PhoneHash (UNIQUE),
PhoneVerifiedAt, IsActive, DeletedAt (+ soft-delete filter); new usr.UserSessions;
usr.UserRoles gains GrantedById/GrantedAt/RevokedAt (revoked grants filtered out globally).
PhoneNumber/Email/NationalId are now encrypted at rest — never query them by equality;
use PhoneHash. Roles seeded: customer, nurse, admin, support, finance, moderation,
super_admin. Config keys added: auth_otp_resend_seconds (120), auth_otp_max_attempts (5),
auth_session_ttl_days (30).
Follow-ups later phases must close
- b3: profiles/patients/addresses/bank accounts;
gender+ names become settable; the/meprofile-completion flags start reading real tables. - b6: Shahkar + KYC set
NationalId/ShahkarVerifiedAt;nurseVerificationStatusbecomes real. - Legacy
UserRefreshTokensstill backs the gRPC path only; retire it when gRPC moves to sessions (or gRPC is dropped). ISmsSender→ real gateway (see mocks-registry row).