HOCO B2B
Team tasksImad

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.

FieldDetails
Task codeMOB-06
PhaseP0 — Foundations (cadrage, UX/UI, architecture & contract)
Product scopeV1
StatusPlanned
StartTue 27 Oct 2026
DueWed 28 Oct 2026
ResponsibleImad — Mobile developer (Flutter)
Co-responsible—
Effort1.75 person-day(s) planned · 5 from scratch · size M
Area · appMobile foundation · mobile
Depends onMOB-03, MOB-05, DES-06
UnblocksMOB-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

#SubtaskOwner
1SessionController (Riverpod) with states unknown, guest, signedIn(user, accountStatus, customerType); restore from secure storage → me.get on launch, offline fallback to the cached meImad
2WelcomeScreen RA-02 with « Se connecter », « Créer un compte » and « Découvrir le catalogue » (guest); guest choice remembered until sign-upImad
3Guest 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-upImad
4SignUpPhoneScreen RA-03: HocoPhoneField (+213), optional invitation code (format AB12-CD34) resolved with invitations.resolve to show « Invité par Karim — Magasin … »; auth.otp.send purpose SIGNUPImad
5OtpScreen 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.modeImad
6Mode 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 duplicateImad
7StoreDetailsScreen 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.resellerImad
8AccountPendingScreen 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 contactImad
9SignInScreen 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 versaImad
10ForgotPassword RA-08 / DA-03: OTP purpose PASSWORD_RESET → auth.password.reset; other sessions revoked; returns to sign-in with a success snackbarImad
11Sign-out (auth.logout) clearing the session and caches; delegate sign-out blocked while the outbox holds unsynced itemsImad
12Design QA of the onboarding and guest screens against DES-06 / DES-11Akram

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

RequirementTitleRelease
AUTH-02Delegate sign-in with phone + password; suspended accounts blocked with an explicit messageV1
AUTH-03Reseller self sign-up: phone verified by OTP, store details, declared customer type (super-gros, demi-gros, détail), password, optional invitation codeV1
AUTH-04Reseller sign-in (phone + password) and password reset by OTPV1
AUTH-05Account claim: a prospect created in the field is activated by the reseller without creating a duplicateV1
AUTH-07Password policy, hashing, OTP expiry/resend cooldown and attempt limitingV1
AUTH-08Account statuses (pending validation, active, suspended, disabled) gate what each user can doV1
AUTH-10Guest mode: the reseller app and web can be browsed without an account; prices stay hidden until the visitor signs up and declares a customer typeV1
AUTH-11Customer type declared at sign-up, reviewed at account validation; any later change is made by the back-office and auditedV1

Screens

ScreenNameApp
RA-02Welcome (sign in, sign up, browse as guest)RA
RA-03Sign-up — phone & invitation codeRA
RA-04OTP verificationRA
RA-05Sign-up — store details, customer type & passwordRA
RA-06Account pending validation / rejectedRA
RA-07Sign inRA
RA-08Forgot passwordRA
DA-02Sign inDA
DA-03Forgot passwordDA
RA-41Guest mode — price lock and sign-up promptsRA

API surface

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.

On this page