SRV-05 — Authentication & sessions (Better Auth)
Phone/email + password sign-in, OTP, reseller sign-up with declared customer type and account claim, guest (anonymous) access rules, password reset/change, bearer sessions for mobile and cookies for web, lockout, status gating, device registration.
| Field | Details |
|---|---|
| Task code | SRV-05 |
| Phase | P0 — Foundations (cadrage, UX/UI, architecture & contract) |
| Product scope | V1 |
| Status | Done (Sun 18 Oct 2026) |
| Start | Sun 18 Oct 2026 |
| Due | Sun 18 Oct 2026 |
| Responsible | Anis — Full-stack developer |
| Co-responsible | — |
| Effort | 1.5 person-day(s) planned · 6 from scratch · size S |
| Area · app | Server · server |
| Depends on | SRV-04 |
| Unblocks | SRV-06 |
| Milestones | — |
Summary
Phone/email + password sign-in, OTP, reseller sign-up with declared customer type and account claim, guest (anonymous) access rules, password reset/change, bearer sessions for mobile and cookies for web, lockout, status gating, device registration.
Description
Authentication serves four audiences with different rules (cahier §3, §5, §6, §10; AUTH-01…AUTH-11, SEC-01, SEC-02): staff sign in by email or phone, delegates and resellers by phone + password, resellers sign up themselves with an OTP-verified phone, and guests browse without any account. This task configures Better Auth with the Prisma adapter — bearer sessions for mobile (30 days sliding), httpOnly cookies on .hoco.dz for the web apps (back-office 7 days) — and adds the HOCO-specific flows: OTP send/verify with the limits of AUTHORING §3, auth.signup.reseller with store details and the declared customer type (SUPER_GROS, DEMI_GROS, DETAIL, AUTH-11) and an optional invitation code, account claim of a field prospect with the same normalised phone (AUTH-05), lockout, status gating (ACCOUNT_PENDING_VALIDATION blocks ordering, prices visible right away by default), and device registration. Guest access is the absence of a session: an optional-auth resolver lets catalog procedures run anonymously and withhold prices (AUTH-10). Out of scope: permissions (SRV-06) and the CRM validation queue (SRV-14).
Subtasks
| # | Subtask | Owner |
|---|---|---|
| 1 | Better Auth setup (Prisma adapter, phone + password and email + password for staff, bearer plugin for mobile, cross-subdomain cookie for web) | Anis |
| 2 | OTP service: 6 digits, 5 min validity, 5 attempts, 1 send per 60 s and 5 per hour per phone, purposes SIGNUP / PASSWORD_RESET / PHONE_CHANGE, SMS adapter | Anis |
| 3 | auth.signup.reseller: verified-phone token, store details, wilaya/commune, required customerType, password, optional invitation code; creates User + Reseller (PENDING_VALIDATION, source SELF_SIGNUP) in one transaction | Anis |
| 4 | Account claim: phone matches a field prospect → attach the user to the existing Reseller, keep the creating delegate and attribution, no duplicate (AUTH-05, CRM-03) | Anis |
| 5 | Lockout after 5 failures for 15 min (429 AUTH_LOCKED), password policy and hashing, password reset by OTP and change with session revocation | Anis |
| 6 | Status gating middleware: PENDING_VALIDATION (browse with prices per catalog.visibleBeforeValidation, no ordering → 403 ACCOUNT_PENDING_VALIDATION), SUSPENDED, DISABLED | Anis |
| 7 | Optional-auth resolver for auth: optional procedures (guest context, no cart, no favorites) | Anis |
| 8 | Sessions list/revoke and sign-out; device registration me.devices.register with FCM token and X-Device-Id; me.get/me.update | Anis |
| 9 | AuditLog entries LOGIN, LOGIN_FAILED, LOGOUT with IP, client app and device | Anis |
| 10 | Flow review for sign-up, claim and guest-to-account transition on mobile | Imad |
Deliverables
- apps/server/src/modules/identity/{auth.ts,otp.service.ts,signup.service.ts,claim.service.ts,sessions.router.ts,me.router.ts}
- apps/server/src/middleware/{optional-auth,status-gate}.ts
- packages/contracts/src/{auth,me}.ts implemented end to end
Acceptance criteria
- Sign-up without customerType fails with 422 VALIDATION_FAILED; a successful sign-up stores the declared type on Reseller.customerType (AUTH-03, AUTH-11)
- Signing up with the phone of an existing field prospect claims that reseller record instead of creating a second one (AUTH-05)
- The 6th wrong password within the window returns 429 AUTH_LOCKED for 15 minutes; OTP limits return 429 OTP_RATE_LIMITED
- A PENDING_VALIDATION reseller can sign in and browse but cart.checkout returns 403 ACCOUNT_PENDING_VALIDATION (AUTH-08)
- A request without a session to catalog.products.list succeeds; the same request to cart.get returns 401 UNAUTHENTICATED (AUTH-10)
- Revoking a session invalidates its bearer token on the next request (AUTH-06)
Requirements covered
| Requirement | Title | Release |
|---|---|---|
| AUTH-01 | Back-office staff sign-in (email or phone + password) | V1 |
| AUTH-02 | Delegate sign-in with phone + password; suspended accounts blocked with an explicit message | V1 |
| AUTH-03 | Reseller self sign-up: phone verified by OTP, store details, declared customer type (super-gros, demi-gros, détail), password, optional invitation code | V1 |
| AUTH-04 | Reseller sign-in (phone + password) and password reset by OTP | V1 |
| AUTH-05 | Account claim: a prospect created in the field is activated by the reseller without creating a duplicate | V1 |
| AUTH-06 | Sessions: expiry, multi-device, listing and revocation, sign-out | V1 |
| AUTH-07 | Password policy, hashing, OTP expiry/resend cooldown and attempt limiting | V1 |
| AUTH-08 | Account statuses (pending validation, active, suspended, disabled) gate what each user can do | V1 |
| AUTH-09 | Device registration for push and minimum-app-version enforcement | V1 |
| SEC-01 | Secure authentication with hashed passwords | V1 |
| SEC-02 | Session expiry, revocation and attempt limiting | V1 |
| AUTH-10 | Guest mode: the reseller app and web can be browsed without an account; prices stay hidden until the visitor signs up and declares a customer type | V1 |
| AUTH-11 | Customer type declared at sign-up, reviewed at account validation; any later change is made by the back-office and audited | V1 |
API surface
| Endpoint group | Operations |
|---|---|
| Authentication & sessions | 10 |
| Me, devices, app config & reference data | 9 |
Test measures
- Integration tests against Postgres and Redis for sign-up, claim, lockout and OTP limits
- Unit tests for phone normalisation to E.164 (+213 forms with and without leading zero)
- E2E: sign up on staging with the SMS simulator, sign in from the web cookie and from a bearer client
Risks
- Better Auth phone flows do not fit the claim/sign-up contract exactly — keep Better Auth for credentials and sessions and implement sign-up/claim as HOCO procedures that call its internal API
- SMS delivery delays in Algeria during OTP — resend cooldown visible in the response and provider fallback planned in SRV-08
KPI
- Delivered by Sun 18 Oct 2026 within 1.5 person-day(s).
- Every acceptance criterion above demonstrated before hand-off; linked requirements' acceptance criteria pass.
- Zero open critical defects, permission gaps or data-integrity regressions at sign-off.
Generated from content/plan/tasks.json by scripts/generate-team-tasks.mjs. Dates are computed by the scheduler (Sunday–Thursday weeks, holidays excluded) — change the plan, not this page.
SRV-04 — Server foundation — Express + oRPC, errors, logging, rate limits, API reference
HTTP app skeleton: oRPC OpenAPI handler under /v1, error envelope, request ids, pino logging, CORS, rate limiting, Idempotency-Key middleware, /health, /openapi.json and the Scalar reference.
SRV-06 — RBAC & data scopes
Role and permission catalogue (module × action), permission guard on every procedure, delegate zone scope and reseller ownership scope as reusable query filters.