MOB-06 — Authentication flows (both apps)
Guest mode (browse without prices), reseller welcome, sign-up with OTP, customer type and account claim, pending-validation screen, sign-in, forgot password; delegate sign-in and reset; session restore and status gating.
| Field | Details |
|---|---|
| Task code | MOB-06 |
| Phase | P0 — Foundations (cadrage, UX/UI, architecture & contract) |
| Product scope | V1 |
| Status | Planned |
| Start | Tue 27 Oct 2026 |
| Due | Wed 28 Oct 2026 |
| Responsible | Imad — Mobile developer (Flutter) |
| Co-responsible | — |
| Effort | 1.75 person-day(s) planned · 5 from scratch · size M |
| Area · app | Mobile foundation · mobile |
| Depends on | MOB-03, MOB-05, DES-06 |
| Unblocks | MOB-08, MRA-01, MRA-09 |
| Milestones | — |
Summary
Guest mode (browse without prices), reseller welcome, sign-up with OTP, customer type and account claim, pending-validation screen, sign-in, forgot password; delegate sign-in and reset; session restore and status gating.
Description
Every reseller and delegate journey starts here, and the 17 Oct decisions change it: the reseller app can be browsed as a guest with prices hidden (AUTH-10, RA-41), and sign-up declares the customer type (SUPER_GROS, DEMI_GROS, DETAIL) that later selects the price (AUTH-11). Scope: reseller welcome (sign in, sign up, browse as guest), the guest price lock and sign-up prompts, sign-up with phone + optional invitation code, OTP, store details with customer type and password, the account-claim path for prospects created in the field (AUTH-05), pending/rejected screens, sign-in, forgot password; delegate sign-in (suspended message) and reset; session restore and status gating in both apps. It uses auth.*, me.get, reference.wilayas|communes|lists and invitations.resolve, and needs SRV-05 behaviour on staging or Prism examples from Anis plus Akram's DES-06 and DES-11 frames. MRA-01…MRA-10 rely on the session/guest state set here, MRA-09 prefills the invitation code, and MOB-08 registers the device after sign-in.
Subtasks
| # | Subtask | Owner |
|---|---|---|
| 1 | SessionController (Riverpod) with states unknown, guest, signedIn(user, accountStatus, customerType); restore from secure storage → me.get on launch, offline fallback to the cached me | Imad |
| 2 | WelcomeScreen RA-02 with « Se connecter », « Créer un compte » and « Découvrir le catalogue » (guest); guest choice remembered until sign-up | Imad |
| 3 | Guest mode RA-41: catalog calls sent without Authorization, PriceText lock « Créez un compte pour voir les prix », SignUpPromptSheet on add-to-cart, favorites, orders and account tabs; the product or route that triggered it is replayed after sign-up | Imad |
| 4 | SignUpPhoneScreen RA-03: HocoPhoneField (+213), optional invitation code (format AB12-CD34) resolved with invitations.resolve to show « Invité par Karim — Magasin … »; auth.otp.send purpose SIGNUP | Imad |
| 5 | OtpScreen RA-04 (shared with password reset): 6-digit field with SMS User Consent autofill, 60 s resend countdown, OTP_INVALID / OTP_EXPIRED / OTP_RESEND_TOO_SOON / TOO_MANY_ATTEMPTS messages; auth.otp.verify → verificationToken + signupContext.mode | Imad |
| 6 | Mode routing after verification: NEW → empty store form, CLAIM → form prefilled with the field record (AUTH-05), EXISTING_ACCOUNT → « Se connecter » / « Mot de passe oublié » instead of a duplicate | Imad |
| 7 | StoreDetailsScreen RA-05: store and owner name, wilaya/commune pickers (reference.wilayas, reference.communes, COMMUNE_NOT_IN_WILAYA), address, business type (BUSINESS_TYPE), required customer-type radio « Super-gros » / « Demi-gros » / « Détail » with a one-line explanation, password with policy errors, terms consent → auth.signup.reseller | Imad |
| 8 | AccountPendingScreen RA-06: « Compte en attente de validation » with the declared type, catalog browsing with that type's prices (decision default: right away, switchable by a flag), checkout blocked with ACCOUNT_PENDING_VALIDATION; rejected variant with the reason and a support contact | Imad |
| 9 | SignInScreen RA-07 and DA-02 (auth.login with app RESELLER_APP or DELEGATE_APP): INVALID_CREDENTIALS, RATE_LIMITED with the Retry-After countdown, ACCOUNT_SUSPENDED explicit message, APP_NOT_ALLOWED when a reseller opens the delegate app or vice versa | Imad |
| 10 | ForgotPassword RA-08 / DA-03: OTP purpose PASSWORD_RESET → auth.password.reset; other sessions revoked; returns to sign-in with a success snackbar | Imad |
| 11 | Sign-out (auth.logout) clearing the session and caches; delegate sign-out blocked while the outbox holds unsynced items | Imad |
| 12 | Design QA of the onboarding and guest screens against DES-06 / DES-11 | Akram |
Deliverables
- apps/reseller/lib/src/features/auth/ (welcome, guest, sign-up, otp, store details, pending, sign-in, forgot password)
- apps/delegate/lib/src/features/auth/ (sign-in, forgot password)
- packages/hoco_core/lib/src/session/session_controller.dart
- integration_test/auth_flow_test.dart in both apps
- Firebase App Distribution build 0.1.0 (staging) of both apps for Akram and Anis
Acceptance criteria
- A guest browses home, catalog, product pages and promotions; no price is ever displayed and nothing can be added to the cart (AUTH-10)
- Sign-up cannot be submitted without a customer type; the created reseller has that customerType and sees its prices right after sign-up while checkout returns ACCOUNT_PENDING_VALIDATION until validation (AUTH-03, AUTH-11)
- A phone already attached to a field prospect goes through CLAIM and does not create a second reseller; an EXISTING_ACCOUNT phone is redirected to sign-in / reset (AUTH-05, CRM-03)
- OTP resend is disabled for 60 s and each OTP error code shows its own message (AUTH-07)
- A suspended delegate or reseller gets an explicit « compte suspendu » message and no session; a reseller in the delegate app gets APP_NOT_ALLOWED (AUTH-02, AUTH-08)
- After a kill and relaunch the session is restored without re-entering credentials; an expired session lands on sign-in
Requirements covered
| Requirement | Title | Release |
|---|---|---|
| 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-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-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 |
Screens
| Screen | Name | App |
|---|---|---|
| RA-02 | Welcome (sign in, sign up, browse as guest) | RA |
| RA-03 | Sign-up — phone & invitation code | RA |
| RA-04 | OTP verification | RA |
| RA-05 | Sign-up — store details, customer type & password | RA |
| RA-06 | Account pending validation / rejected | RA |
| RA-07 | Sign in | RA |
| RA-08 | Forgot password | RA |
| DA-02 | Sign in | DA |
| DA-03 | Forgot password | DA |
| RA-41 | Guest mode — price lock and sign-up prompts | RA |
API surface
| Endpoint group | Operations |
|---|---|
| Authentication & sessions | 10 |
| Me, devices, app config & reference data | 9 |
| Invitations & attribution (public + reseller) | 3 |
Test measures
- integration_test of guest → sign-up (NEW) → pending → browse with prices against Prism examples
- Widget tests of each OTP and sign-in error state and of the CLAIM / EXISTING_ACCOUNT branches
- Unit tests of SessionController transitions (guest → signedIn, status change from me.get)
Risks
- The 'prices before validation' decision may flip — read it from a flag (appConfig public settings) so the change needs no release
- SMS delivery delays in Algeria make users resend too early — show the countdown and the masked phone clearly, and keep the code valid for its full 5 minutes
KPI
- Delivered by Wed 28 Oct 2026 within 1.75 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.
MOB-05 — Navigation, app shells & system states
go_router with auth guard, bottom-navigation shells for both apps, deep-link routes, splash/version check, offline/maintenance/forced-update/error/empty states.
MOB-08 — Push notifications foundation
Firebase projects per flavor, FCM token registration, foreground/background handling, notification → deep-link routing.