Aller au contenu

Design Spec — Identity (AUTH)

Module : identity (story prefix AUTH) Side(s) : [CUSTOMER] + [OPERATOR] + [ADMIN] (Platform admin shares the operator UX surface per PRD D-3). Date : 2026-05-22 Authored by : DESIGNER session, sur PRD docs/prd/identity-prd.md v2 + envelope V0 (v0.0.0 → v0.1.0). Tokens : frontend/_shared/tokens.css + thèmes frontend/customer/src/theme/theme-customer.css (chaleureux, basse densité) et frontend/operator/src/theme/theme-operator.css (sobre, dense). Scaffolds existants : frontend/customer/ et frontend/operator/ (Vite + React 19.2, signed-link boundary + auth boundary déjà câblés). Les écrans ci-dessous viennent se poser sur ces shells — ils n'introduisent pas de nouveau host.

Légende des side-tags dans le doc : [C] = Customer (assuré, lien signé), [O] = Operator (Keycloak), [A] = Cabinet admin (Keycloak, sur la console opérateur), [PA] = Platform admin (Keycloak, surface = console opérateur, label « Papillon Admin »).


⚠️ Focus session : v0.0.0 uniquement (verrouillé 2026-05-22)

Le reste du document décrit la cible V1 complète, qui sert de référence. Les sections 4–13 couvrent donc des écrans qui ne seront PAS livrés en v0.0.0. La table ci-dessous fixe le périmètre design réellement en chantier maintenant.

Périmètre v0.0.0 (cf. PRD § V0 Envelope + prd-v0.md §4, §5.4, §5.8)

Surface v0.0.0 ? Notes
[O] Login étape A (identifiant + mot de passe — ROPC) IN Keycloak >= 26.6, realm papillon, flow ROPC sans MFA. C'est l'unique chemin d'auth opérateur v0.0.0.
[O] Login étape B (MFA SMS OTP) ❌ OUT MFA reportée — V0 §6, drapeau feature.whatsapp.operator-mfa.enabled=false. Repris en V1.
[O] Remember-device 15 jours ❌ OUT Couplé à MFA, donc reporté V1.
[O] Step-up modal ❌ OUT Couplé à MFA. V1.
[O] Bypass MFA admin (code de secours) ❌ OUT Couplé à MFA. V1.
[O] Bannière d'inactivité 30 min IN PRD §17 reste actif (auto-logout). Pas couplé à MFA. À livrer dès v0.0.0.
[O] Comptes inactif / désactivé / verrouillé IN Copys côté login (R-OP-008/009/013) dès v0.0.0.
[O] Mot de passe oublié ❌ OUT (V1) Pas dans le périmètre v0.0.0. En attendant : un admin de plateforme (OPS runbook) réinitialise out-of-band.
[A] Activation invitation (1er CABINET_ADMIN) IN TenantOnboarded → AUTH provisionne org Keycloak + 1er CABINET_ADMIN + envoi e-mail invitation NOTIF (R-ADM-007 / D-1). L'écran /activate?token=… est donc requis. Sans saisie de numéro MFA (MFA reportée) — l'écran demande seulement mot de passe + confirm.
[A] Liste utilisateurs + drawer + invite, désactivation, role-change ❌ OUT du périmètre AUTH v0.0.0 Pas mentionné dans l'envelope v0.0.0 pour AUTH (seul le bootstrap du 1er CABINET_ADMIN est livré). En v0.0.0 le cabinet fonctionne avec un seul CABINET_ADMIN provisionné automatiquement. L'invite d'opérateurs supplémentaires arrive ultérieurement. À reconfirmer avec le PRODUCT_OWNER.
[C] Landing + challenge (DOB / RCCM / 4 derniers chiffres) ❌ OUT Côté CP, le câblage end-to-end du signed-link tombe en v0.0.1 (token actif). En v0.0.0, le SignedLinkService existe en backend (hash SHA-256 stocké) mais n'est pas exposé en UI. Aucun écran customer à livrer v0.0.0.
[C] Tous les états terminaux (lockout / expired / revoked / paid / suspended) ❌ OUT Idem. Verrouillage / expiration n'arrivent qu'à v0.1.0 ; révocation / payé arrivent en V1.
[C] Bandeau low-network ❌ OUT (v0.0.0) Aucun écran customer v0.0.0.
[PA] Login IN Même écran que [O] (ROPC sans MFA). Badge « Papillon Admin » dans le top-bar. Step-up cross-tenant ❌ OUT (couplé MFA).
[O/A] Bandeau « Cabinet suspendu » ❌ OUT (v0.0.0) TENANT lifecycle (suspend / reactivate) n'est pas activé en v0.0.0 — TenantSuspended n'est pas émis. À livrer plus tard.

Écrans à livrer en v0.0.0 (récapitulatif)

  1. Login Operator/PA (étape A seulement) — section 5.1 du présent doc, moins : l'étape B, le remember-device, le step-up. Bouton « Mot de passe oublié » masqué en v0.0.0 (renvoie OPS runbook).
  2. Activation invitation CABINET_ADMIN — section 5.5 du présent doc, moins le bloc « Numéro pour recevoir les codes (SMS) » + checkbox WhatsApp. Reste : pwd + confirm + bouton « Activer mon compte ».
  3. Bannière d'inactivité 30 min — section 5.8 du présent doc, telle quelle (la session timeout n'est pas couplée à MFA).
  4. Écrans cul-de-sac comptes — section 5.4 (compte non activé / désactivé / verrouillé), telles quelles.

Décisions UX issues du focus v0.0.0

  • D-v0.0.0-1 : sur l'activation, ne pas demander de numéro MFA v0.0.0 (sinon on collecte une donnée non utilisée → minimisation ARTCI). Le numéro sera collecté quand MFA arrivera (V1) — soit via un écran « complétez votre profil sécurité » au prochain login, soit via l'admin runbook. Décision V1 différée.
  • D-v0.0.0-2 : sur le login, masquer « Mot de passe oublié » v0.0.0 plutôt que de l'afficher en lien mort. Don Norman mapping : un bouton qui ne fait rien casse la confiance.
  • D-v0.0.0-3 : sur l'écran de login, pas de checkbox « se souvenir de cet appareil » v0.0.0 (couplée au cookie remember-device qui n'arrive qu'avec MFA). L'utilisateur ressaisit pwd à chaque session, point — c'est tolérable sur les volumes pré-prod.
  • D-v0.0.0-4 : aucun écran customer livré v0.0.0. Le frontend customer/ reste sur le shell de boundary signed-link sans écrans AUTH ; les URL /r/{token} retournent un placeholder « En cours de déploiement » côté CP (à coordonner avec CP, hors AUTH).
  • D-v0.0.0-5 : pas de badge « Papillon Admin » distinct v0.0.0 si la seule différence est cosmétique. Mais on garde la sémantique côté token : tenant_id=null pour PLATFORM_ADMIN. La distinction visuelle peut tomber sous V1 si l'OPS n'en a pas besoin. Provisoire : badge IN dès v0.0.0 pour éviter une erreur d'OPS en cross-tenant.

Wireframes runnables prioritaires pour v0.0.0

Phase 3 (/wireframe identity <STORY-ID>) doit produire ces 2 prototypes seulement :

  1. AUTH-001 — Login Operator/PA, étape A unique (pwd-only, sans MFA), avec les 4 sous-états : default / loading / error-credentials / account-state (inactif/désactivé/verrouillé). Toggle dev-only entre les sous-états + toggle PA on/off pour le badge.
  2. AUTH-001 activate variant — Activation invitation CABINET_ADMIN (pwd + confirm), avec les 4 sous-états : default / token-expired / token-consumed / already-active.

Les autres prototypes décrits en §13 et Annexe B du présent doc restent valides pour V1 mais n'ont pas vocation à être générés tant que la sous-version cible n'est pas active.


Note sur l'interview UX

Cette session a été lancée en mode autonome (/design identity … do it straight to the end. Do not wait for my approval.). Les décisions UX significatives sont donc prises et documentées en ligne dans chaque section sous le marqueur **Décisions appliquées**. Une session future peut auditer / contester chacune.


Sommaire

  1. Principes directeurs AUTH
  2. Architecture d'information & cartes de surfaces
  3. User Flow Diagrams (Mermaid)
  4. Wireframes — côté [CUSTOMER]
  5. Wireframes — côté [OPERATOR] + [ADMIN]
  6. Wireframes — côté [PLATFORM_ADMIN] (delta vs operator)
  7. Spécifications d'interaction — 5 états par écran
  8. Comportement responsive
  9. Comportement connectivité ([CUSTOMER] slow-3G + drop + reload + link-reuse)
  10. Micro-copy française (référentiel canonique)
  11. Spécifications composants (ré-utilisation + nouveaux)
  12. UI de conformité (CIMA 01-24, ARTCI, law 2013-450)
  13. Mapping écrans ↔ stories AUTH

1. Principes directeurs AUTH

Côté [CUSTOMER]Renouveler en confiance, depuis son téléphone

  • Un seul geste par écran. La landing du lien signé n'a qu'une seule action : saisir la date de naissance (ou le RCCM) et valider. Pas de menu, pas de tabs, pas d'onboarding.
  • Identité du cabinet au-dessus de la ligne de flottaison, sur chaque écran et chaque état (AUTH-NFR-10). Le logo, le nom commercial, et le téléphone d'agence sont collés en haut.
  • Pas de spinner aveugle — un texte court explique ce qui se passe (« Vérification du lien… »). Le bouton de soumission affiche son état loading (Button du DS).
  • Tous les chemins « infortunés » sont des écrans à part entière : expiré, verrouillé 30 min, verrouillé définitif, cabinet suspendu, déjà payé. Chacun fournit une sortie (numéro d'agence cliquable tel:).
  • Aucun mot de passe, aucun PIN, aucun fichier à téléverser sur le portail customer V1.
  • Mention « Service fourni par Papillon Collection Solution » discrète en pied de page (CIMA 01-24 R-1 du mémo v2 — surface fournisseur technique).

Côté [OPERATOR] / [ADMIN]Une connexion qui n'agace pas dix fois par jour

  • « Remember device 15 jours » par défaut coché à la première saisie OTP. Hick's Law : la friction d'un OTP quotidien est punitive en agence.
  • Step-up = modale, jamais une nouvelle page. Une seule donnée (OTP), un seul bouton. Le contexte de l'action sensible (« Valider la campagne Mai 2026 ») reste visible derrière la modale.
  • Avertissement d'inactivité = bannière in-page non-modale + countdown visible. Ne vole pas le focus si l'opérateur est en train de taper.
  • Tous les états d'erreur de login pointent vers un humain : « Contactez votre administrateur. » Pas de cul-de-sac.
  • MFA bypass code : friction délibérée pour l'admin (double confirmation, raison obligatoire, code envoyé à son numéro). Forcer la sécurité par le design.

Côté [PLATFORM_ADMIN]

  • Identique à l'operator dans les écrans d'auth, avec un badge Papillon Admin dans le top-bar pour rappeler qu'on est cross-tenant (pas dans un cabinet). Évite l'erreur de croire qu'on agit pour un cabinet précis.
  • Step-up sur toute action mutante cross-tenant (TENANT_CREATE, SUSPEND, …) — même modale que l'opérateur, copy adaptée (« Action sensible plateforme »).

2. Architecture d'information & cartes de surfaces

2.1 Surfaces côté [CUSTOMER]

/r/{signedLinkToken}                          ← unique URL d'entrée customer
├── Landing + challenge DOB         (assuré individuel)            ← AUTH-CP-02
│   ├── État : challenge en cours (tentative 1..5)
│   ├── État : lockout 30 min                                       ← AUTH-CP-04
│   ├── État : révoqué définitif                                    ← R-CP-013
│   ├── État : lien expiré (TTL 72h)                                ← AUTH-CP-05
│   ├── État : déjà payé (auto-révoqué)                             ← R-CP-014 (a)
│   ├── État : cabinet suspendu / indisponible                      ← R-OP-017 / R-CP-020
│   └── État : low-network bandeau (overlay non-bloquant)
└── Landing + challenge RCCM        (souscripteur personne morale)  ← AUTH-CP-03

Aucune autre URL AUTH côté customer. Après succès du challenge, la nav passe au domaine customer-portal (hors module AUTH).

2.2 Surfaces côté [OPERATOR] + [ADMIN]

/login                                                              ← AUTH-OP-02
├── Étape A : identifiant + mot de passe
├── Étape B : OTP SMS (avec resend → SMS-2 → WhatsApp)               ← AUTH-OP-03
│   └── Toggle « Se souvenir de cet appareil 15 jours »
└── Étape B' (alternate) : saisie du code de secours (bypass)        ← R-OP-007 / R-OP-004

/login/password-forgotten                                            ← AUTH-ADM-03
├── Étape 1 : saisie identifiant
└── Étape 2 : choisir nouveau mot de passe + OTP fresh

/activate?token=…                                                    ← AUTH-OP-01
├── État : token valide → choisir mot de passe + numéro MFA
├── État : token expiré / consommé
└── État : compte déjà activé

(Overlays modaux sur n'importe quel écran de la console)
├── Modale Step-up MFA                                               ← AUTH-OP-05 / R-OP-010
├── Bannière d'inactivité (30 min idle, 1 min countdown)             ← AUTH-OP-06
├── Bannière « Session 8h atteinte, reconnectez-vous »               ← R-OP-011
└── Bandeau « Cabinet suspendu » (sticky topbar warning)             ← AUTH-ADM-05

/admin/utilisateurs                                                  ← AUTH-ADM-01 / 02 / 07
├── Liste + filtres rôle / agence / statut (déjà couvert op-design §4.12)
├── Drawer détail utilisateur (rôle, agence_scope, MFA, statut)
│   ├── Action : Inviter (modale)
│   ├── Action : Changer rôle / agence_scope                         ← step-up requis
│   ├── Action : Émettre code de secours MFA                         ← AUTH-OP-04
│   ├── Action : Réinitialiser MFA phone                             ← step-up requis
│   └── Action : Désactiver (modale 2 étapes + raison)               ← AUTH-OP-07
└── Modale « Code de secours émis » — code 6 chiffres + TTL 15 min   ← R-OP-007

La liste + modale d'invite simple sont déjà esquissées dans op-design.md §4.12. Le présent doc complète ce que op-design ne porte pas : le drawer détail, les actions MFA-bypass, la désactivation avec motif, la réinit MFA, la modale step-up.

2.3 Surfaces côté [PLATFORM_ADMIN]

/login                  ← identique [O], pas de tenant sélectionné, label « Papillon Admin »
/admin/platform/…       ← (out-of-scope identity ; relève de TENANT)
(overlays)
└── Modale Step-up MFA (variante copy « Action sensible plateforme »)

3. User Flow Diagrams (Mermaid)

flowchart TD
  start([SMS / WhatsApp / Email reçu]) --> click[Click sur /r/{token}]
  click --> verify{Vérification signature<br/>+ TTL + révocation + tenant.status}
  verify -- "tenant != ACTIVE" --> tenantDown[Écran : Service indisponible]
  verify -- "lien expiré (>72h)" --> expired[Écran : Lien expiré]
  verify -- "lien révoqué (réémis)" --> revoked[Écran : Lien désactivé]
  verify -- "auto-révoqué (payé)" --> paid[Écran : Tous contrats déjà payés]
  verify -- "permanently revoked (3 cycles)" --> permaRev[Écran : Lien désactivé sécurité]
  verify -- "OK" --> challenge[Landing : Identité cabinet + challenge DOB/RCCM]
  challenge -- "low-network drop" --> resume{{Reload du lien : counters serveur préservés}}
  resume --> challenge
  challenge -- "saisie correcte" --> session[Session ouverte → Portail Customer (hors AUTH)]
  challenge -- "saisie fausse (n<5)" --> retry[Erreur inline « Tentative n/5 »<br/>focus reste sur l'input]
  retry --> challenge
  challenge -- "5e échec consécutif" --> lockout[Écran : Verrouillage 30 min<br/>compteur cycle ++]
  lockout -- "30 min écoulées,<br/>cycle<3" --> challenge
  lockout -- "cycle == 3" --> permaRev
  expired --> exit1([tel:agencyPhone])
  revoked --> exit1
  paid --> exit1
  permaRev --> exit1
  tenantDown --> exit1

3.2 Operator login + MFA (avec fallback WhatsApp + bypass)

flowchart TD
  start([Page /login]) --> creds[Saisie identifiant + mot de passe]
  creds -- "invalide" --> credsErr[Erreur générique « Identifiant ou mot de passe incorrect »]
  credsErr --> creds
  creds -- "OK" --> rdCheck{Cookie remember-device<br/>15 jours valide ?}
  rdCheck -- "oui" --> ok[Session ouverte → /dashboard]
  rdCheck -- "non" --> otpA[OTP SMS envoyé, prompt 6 chiffres]
  otpA -- "OTP OK + tick « se souvenir »" --> okRD[Cookie 15j émis → /dashboard]
  otpA -- "OTP OK sans tick" --> ok
  otpA -- "OTP KO 1/3" --> otpRetry[Retry OTP — toujours même code]
  otpRetry -- "OTP KO 3/3" --> mfaLocked[MFA verrouillée 15 min<br/>Bouton « code de secours » visible]
  otpA -- "Pas de réception, click resend" --> otpB[2e SMS envoyé]
  otpB -- "Pas de réception, click resend, whatsapp_enabled" --> otpW[OTP WhatsApp envoyé]
  otpB -- "Pas de réception, click resend, !whatsapp_enabled" --> noFallback[Affiche : « Demandez un code de secours »]
  otpW -- "OTP OK" --> ok
  mfaLocked --> bypassEntry[Saisie code bypass (issu par admin)]
  noFallback --> bypassEntry
  bypassEntry -- "OK + non expiré + non consommé" --> ok
  bypassEntry -- "KO / consommé / expiré" --> bypassErr[Erreur + lien « Demander un nouveau code »]

3.3 Operator step-up sur action sensible (in-place modal)

flowchart LR
  trigger[Clic sur action sensible<br/>e.g. « Valider campagne »] --> stepup[Modale step-up : OTP SMS fresh]
  stepup -- "OTP OK" --> proceed[Action exécutée + audit log step_up=true]
  stepup -- "OTP KO 1..2/3" --> stepup
  stepup -- "OTP KO 3/3" --> aborted[Action abandonnée<br/>session opérateur préservée]
  stepup -- "Cancel" --> aborted

3.4 Tenant suspended cascade — vu côté customer + opérateur

flowchart TD
  evt([TenantSuspended reçu]) --> cache[(Invalidate cache tenant-status<br/>+30s propagation)]
  cache --> op[Console operator : ↓]
  cache --> cust[Portail customer : ↓]
  op --> opAttempt[Login opérateur du cabinet]
  opAttempt --> opCopy[« Cabinet suspendu. Contactez le support Papillon. »]
  op --> opSession[Sessions opérateur en cours]
  opSession --> killed[Toutes sessions tuées + cookies remember-device invalidés ≤30s]
  cust --> custClick[Click sur un lien signé du cabinet]
  custClick --> custCopy[HTTP 503 + « Service temporairement indisponible.<br/>Contactez votre cabinet. »]

3.5 Operator invitation → activation

flowchart TD
  admin[CABINET_ADMIN clique « Inviter »] --> form[Modale invite : nom + email/sms/wa + rôle + agences + MFA phone]
  form --> stepup[Step-up MFA]
  stepup -- "OK" --> dispatch[NOTIF dispatch sur canal choisi]
  dispatch --> user[User reçoit le lien d'activation]
  user --> click[Click /activate?token=…]
  click --> tokOK{Token valide ?}
  tokOK -- "expiré (>7j)" --> tokExp[« Lien expiré. Demandez un nouvel accès. »]
  tokOK -- "déjà consommé" --> tokUsed[« Lien déjà utilisé. »]
  tokOK -- "OK" --> setPwd[Formulaire : mot de passe (≥8) + confirm + numéro MFA]
  setPwd --> setOk[Account ACTIVE → redirection /login]

4. Wireframes — côté [CUSTOMER]

Cible : 360 px portrait (Android entrée de gamme). Toutes les cibles tactiles ≥ 48 px. Budget initial < 200 KB gz (AUTH-NFR-01). Pas de SDK lourd, pas d'image > 10 KB.

4.1 Écran : Landing + Challenge DOB (AUTH-CP-02)

Décisions appliquées : - Décision : un seul écran, un seul input (vs wizard 2 étapes « Confirmer identité » → « Saisir DOB »). Le wizard rajoute un tap et casse le mental model — l'utilisateur a déjà cliqué le lien, c'est l'engagement. Hick's Law : moins de choix = plus rapide. - Décision : header identité cabinet collé en haut, persistant sur tous les états, pas un placard escamotable. C'est la pièce de confiance ; elle ne doit jamais disparaître (AUTH-NFR-10). - Décision : DOB en <input type="date"> ET pattern texte secondaire JJ/MM/AAAA — sur Android entrée de gamme, le date-picker natif est très accessible ; sur navigateurs anciens (Opera Mini), le fallback texte assure que la saisie reste possible. Aucune lib JS de date. - Décision : pas d'illustration. Réduit le poids (< 200 KB), enlève le risque « marketing-y » sur un domaine d'assurance. Le logo cabinet suffit comme signal visuel.

360 px portrait
┌───────────────────────────────────────────────┐
│  [logo cabinet]   Cabinet AXA Plateau          │  ← Header identité (AUTH-NFR-10)
│                   +225 27 22 41 12 34 ☎️       │     persistant, hauteur 64 px
├───────────────────────────────────────────────┤
│                                                │
│  Bonjour,                                      │  H2 — 20 px
│  Pour accéder à vos contrats,                  │  Body — 16 px
│  entrez votre date de naissance.               │
│                                                │
│  Date de naissance                             │  Label
│  ┌─────────────────────────────────────────┐   │
│  │  JJ / MM / AAAA                         │   │  Input — 48 px haut
│  └─────────────────────────────────────────┘   │
│                                                │
│  ┌─────────────────────────────────────────┐   │
│  │            Voir mes contrats            │   │  Button primary — 48 px
│  └─────────────────────────────────────────┘   │
│                                                │
│  🔒 Connexion sécurisée                        │  Caption — 12 px (rassurance)
│                                                │
├───────────────────────────────────────────────┤
│  Besoin d'aide ? Appelez votre agence          │
│  📞 +225 27 22 41 12 34                        │  Lien tel: cliquable
│                                                │
│  Mentions légales · Politique de confidentialité│  Footer (modales)
│  Service fourni par Papillon Collection Solution│
└───────────────────────────────────────────────┘

Composants : Card (sans header), FormField, Input type=date, Button variant=primary size=lg fullWidth, plus deux nouveaux composants IdentityHeader (cabinet) et LegalFooter.

4.2 État : Saisie fausse — tentative n/5

[Header identité]
Date de naissance
┌─────────────────────────────────────────┐
│  15 / 03 / 1985                         │  ← input en erreur (border --color-error)
└─────────────────────────────────────────┘
⚠️ La date saisie ne correspond pas.
   Vérifiez votre date de naissance et réessayez.
   Tentative 2 / 5.                            ← role="alert" aria-live=polite
[Voir mes contrats] (réactivé)

Le compteur reste tendre tant que n < 4 (couleur warning, --color-warning). Au tour 4/5, on bascule en --color-error et on ajoute un texte secondaire : « Encore une erreur et l'accès sera bloqué 30 minutes ».

4.3 État : Lockout 30 min (AUTH-CP-04, R-CP-010)

Décisions appliquées : - Décision : countdown live affiché (« Réessayez dans 27 min ») — donne un horizon concret. Cognitive load : on ne fait pas calculer l'utilisateur. - Décision : pas de bouton « Réessayer maintenant » — il est désactivé jusqu'à expiration ; le tap sur input est ignoré, message inline « Nouveau test possible dans 27 min ». - Décision : numéro d'agence clé en main tel: au cas où l'utilisateur veut sortir.

[Header identité]

   ⏱️                                          ← icône large (svg < 1 KB)
   Trop de tentatives

   Pour des raisons de sécurité,
   l'accès est mis en pause.

   Réessayez dans 27 min.
   ────────────────────────────────────
   En cas de doute, appelez votre conseiller :
   📞 +225 27 22 41 12 34          (tel:)

4.4 État : Lien expiré (TTL >72h)

[Header identité]

   🕓
   Ce lien a expiré

   Pour des raisons de sécurité,
   ce lien n'est plus valide.

   Contactez votre conseiller pour en
   recevoir un nouveau :
   📞 +225 27 22 41 12 34          (tel:)

4.5 État : Lien désactivé sécurité (revoked permanently, R-CP-013)

Même structure, copy = auth.customer.permanently_revoked :

   🔒
   Lien désactivé

   Pour des raisons de sécurité,
   ce lien a été désactivé.

   Contactez votre conseiller pour
   reprendre votre dossier :
   📞 +225 27 22 41 12 34

4.6 État : Lien désactivé — réémis par l'opérateur (revoke_reason=REISSUED)

   ✉️
   Un nouveau lien vous a été envoyé

   Ce lien a été remplacé par un nouveau,
   envoyé par votre conseiller.

   Vérifiez vos messages
   (WhatsApp, SMS ou e-mail).

   Sans nouvelles, appelez :
   📞 +225 27 22 41 12 34

4.7 État : Déjà payé (auto-révoqué après paiement complet)

   Tous vos contrats sont en règle

   Vous avez déjà payé l'ensemble des
   contrats de cette échéance. Merci.

   Un reçu vous a été envoyé par e-mail.
   ────────────────────────────────────
   📞 +225 27 22 41 12 34

4.8 État : Cabinet suspendu / indisponible (R-OP-017, R-CP-020)

Aucune fuite d'information sur le motif — la copy générique 503 est identique pour SUSPENDED, DELETED, PENDING (R-OP-017).

   ⏸️
   Service temporairement indisponible

   Veuillez contacter votre cabinet.

   📞 +225 27 22 41 12 34

4.9 État : Landing — challenge RCCM (AUTH-CP-03)

Identique à 4.1, sauf : - Prompt : « Pour accéder aux contrats de SARL FANGOMA NÉGOCES, entrez le numéro RCCM de l'entreprise. » - Input texte (autocapitalize="characters", inputmode="text"), label « Numéro RCCM », placeholder vide. Hint : « Insensible à la casse et aux espaces. » - Le nom de la raisonSociale vient du customer record (jamais en dur).

4.10 État : Low-network / drop / reload — overlay bandeau

N'EST PAS un écran offline-first. C'est un bandeau information sur n'importe quel écran customer pendant que le réseau hoquette. Le state du challenge est serveur-side : un reload du lien préserve compteurs + cycle (R-CP-002 + §C.4 du PRD).

┌───────────────────────────────────────────────┐
│  [logo]   Cabinet AXA …                        │
│                                                │
│  📶 Connexion lente détectée. Vous pouvez     │  ← banner --color-warning-light
│     fermer cette page et revenir plus tard,   │     hauteur ~ 48 px, dismissable
│     vos tentatives sont conservées.    [×]   │     role="status", aria-live=polite
├───────────────────────────────────────────────┤
│  …(contenu normal)…                            │
└───────────────────────────────────────────────┘

Trigger : navigator.connection.effectiveType ∈ {slow-2g, 2g, 3g} OU latence d'une requête /auth/link/verify > 3 s. Désactivé sur 4g+.


5. Wireframes — côté [OPERATOR] + [ADMIN]

Cible : 1024 px+ desktop, mais accessibilité tablette 768 (cas réel : tablette en agence). 1440 = breakpoint dense.

5.1 Login étape A — identifiant + mot de passe (AUTH-OP-02)

Décisions appliquées : - Décision : un seul card centré, max 420 px de large. Don Norman mapping : c'est une boîte de dialogue mentale, pas une page de marque. - Décision : pas de logo Papillon dominant en signature visuelle. La marque est en small caps en pied du card. La console opérateur est neutre par design — c'est l'identité du cabinet qui dominera plus tard (top-bar de la console). - Décision : input mot de passe avec eye-toggle natif (réutilise Input type=password du DS). - Décision : lien « Mot de passe oublié » sous le bouton, en ghost. Fitts : pas trop loin.

1024 px+ (centré max 420 px)
                  ┌─────────────────────────────────────┐
                  │ ▸ Console opérateur                  │  ← optionnel : badge tenant
                  │                                      │     resolved by realm domain
                  │ Connexion                            │  H1
                  │                                      │
                  │ Identifiant                          │
                  │ [_____________________]              │
                  │                                      │
                  │ Mot de passe                         │
                  │ [__________________] 👁              │
                  │                                      │
                  │ [        Se connecter        ]       │  Button primary fullWidth
                  │                                      │
                  │             Mot de passe oublié ?    │  Button ghost
                  │                                      │
                  │ ─────────────────────────────────    │
                  │   Papillon Collection Solution       │  caption
                  └─────────────────────────────────────┘

5.2 Login étape B — MFA SMS OTP + remember-device

Décisions appliquées : - Décision : 6 inputs single-digit autosplit vs 1 input long. L'autosplit est universel sur les apps bancaires de la zone CIMA ; les utilisateurs sont familiers. Tap-target : chaque case ≥ 44 px. - Décision : checkbox « Se souvenir de cet appareil 15 jours » cochée par défaut. Calcul produit : la sécurité du device-bind est dans la cinématique de bind (UA + IP fingerprint), pas dans l'opt-in. Décocher est explicite pour les machines partagées. - Décision : countdown visible (« Renvoyer le code dans 47 s ») au lieu d'un bouton désactivé silencieux. Donne un horizon. - Décision : « Code reçu par SMS » → « par WhatsApp » est un bandeau au-dessus du form**, pas un changement de wording furtif. L'utilisateur sait pourquoi son téléphone vient de bipper différemment.

                  ┌─────────────────────────────────────┐
                  │ Code de vérification                 │  H1
                  │                                      │
                  │ Un code à 6 chiffres a été envoyé    │
                  │ par SMS au +225 •• •• •• 12 34       │  numéro partiellement masqué
                  │                                      │
                  │ [ ][ ][ ][ ][ ][ ]                   │  6 inputs auto-advance
                  │                                      │
                  │ ☑ Se souvenir de cet appareil 15 jours│
                  │                                      │
                  │ [         Vérifier            ]      │  Button primary
                  │                                      │
                  │ Renvoyer le code dans 47 s           │  countdown then link
                  │                                      │
                  │ ←  Revenir à l'identifiant           │  Button ghost
                  └─────────────────────────────────────┘

5.2.bis : variante après resend WhatsApp

                  ┌─────────────────────────────────────┐
                  │ 💬 Code envoyé par WhatsApp.         │  ← Bandeau info --color-info-light
                  │    Vérifiez votre application.       │     role="status"
                  ├─────────────────────────────────────┤
                  │ Code de vérification                 │
                  │ [ ][ ][ ][ ][ ][ ]                   │
                  │ …                                    │
                  └─────────────────────────────────────┘

5.2.ter : variante « pas de WhatsApp, demandez un bypass »

Bandeau warning au-dessus du form :

   ⚠️ Le code n'arrive pas ? Demandez à votre administrateur
      un code de secours, puis saisissez-le ici.
      [ J'ai un code de secours → ]

Le lien bascule sur le même form de saisie (6 chiffres), avec un sous-titre adapté : « Code de secours (6 chiffres, valable 15 min) ».

5.3 État : MFA verrouillée 15 min (R-OP-006, 3 OTP KO)

Card final :

                  ┌─────────────────────────────────────┐
                  │ 🔒 Vérification verrouillée          │
                  │                                      │
                  │ Trop de codes incorrects.            │
                  │ Réessayez dans 14 min 32 s.          │  countdown live
                  │                                      │
                  │ Sinon, demandez à votre administrateur│
                  │ un code de secours.                  │
                  │                                      │
                  │ [ J'ai un code de secours → ]        │  Button secondary
                  │ ←  Revenir à la connexion            │  Button ghost
                  └─────────────────────────────────────┘

5.4 État : Compte non activé / désactivé / verrouillé

Trois copys distincts (cf. §10), même squelette : card centré, icône, copy, deux boutons (« Contactez votre administrateur » = ouvrir mailto:; « Retour » = revenir login). PAS de step-up offert ici, par design — c'est un cul-de-sac contrôlé.

5.5 Activation de l'invitation (AUTH-OP-01)

Décisions appliquées : - Décision : un seul écran (vs wizard 2 étapes). Choisir un mot de passe + saisir un numéro MFA = 2 inputs ; le wizard ajouterait friction sans bénéfice. Don Norman mapping. - Décision : règle de mot de passe affichée AVANT la saisie, et validée live à mesure que l'utilisateur tape. Réduit le rebond « j'ai validé mais erreur ». - Décision : numéro MFA par défaut au format +225 avec masque (+225 __ __ __ __ __). Smart default.

                  ┌──────────────────────────────────────────┐
                  │ Bienvenue, Marie KONÉ                     │  H1
                  │ Cabinet AXA Plateau                       │  caption
                  │                                           │
                  │ Choisissez votre mot de passe             │
                  │ [___________________] 👁                  │
                  │ ✔ Au moins 8 caractères                   │  live checklist
                  │                                           │
                  │ Confirmez votre mot de passe              │
                  │ [___________________] 👁                  │
                  │                                           │
                  │ Numéro pour recevoir les codes (SMS)      │
                  │ [+225 __ __ __ __ __]                     │
                  │ ☑ Ce numéro reçoit aussi WhatsApp         │
                  │                                           │
                  │ [        Activer mon compte        ]      │
                  │                                           │
                  └──────────────────────────────────────────┘

5.5.bis : États d'erreur token

  • Token expiré : « Lien d'invitation expiré ou déjà utilisé. Demandez un nouvel accès à votre administrateur. »
  • Token consommé : même copy (anti-énumération).
  • Compte déjà activé : « Votre compte est déjà actif. [Se connecter →] »

5.6 Mot de passe oublié (AUTH-ADM-03)

Étape 1 — saisir identifiant :

                  ┌─────────────────────────────────────┐
                  │ Mot de passe oublié                  │  H1
                  │                                      │
                  │ Entrez votre identifiant. Un lien    │
                  │ de réinitialisation vous sera envoyé │
                  │ par e-mail (ou SMS si pas d'e-mail). │
                  │                                      │
                  │ [_____________________]              │
                  │                                      │
                  │ [    Envoyer le lien    ]            │
                  │ ←  Annuler                           │
                  └─────────────────────────────────────┘

Réponse SUCCÈS générique (anti-énumération) : « Si un compte existe pour cet identifiant, un lien vient d'être envoyé. Vérifiez vos messages. »

Étape 2 — nouveau mot de passe + OTP fresh (atteinte via lien) :

                  ┌─────────────────────────────────────┐
                  │ Réinitialisation du mot de passe     │
                  │                                      │
                  │ Nouveau mot de passe                 │
                  │ [___________________] 👁             │
                  │ ✔ Au moins 8 caractères              │
                  │ Confirmation                         │
                  │ [___________________] 👁             │
                  │                                      │
                  │ Code SMS reçu maintenant             │
                  │ [ ][ ][ ][ ][ ][ ]                   │
                  │                                      │
                  │ [    Mettre à jour    ]              │
                  └─────────────────────────────────────┘

5.7 Step-up modal (AUTH-OP-05 + R-OP-010)

Décisions appliquées : - Décision : modale, jamais une nouvelle page — préserve le contexte. L'opérateur voit ce qu'il s'apprête à confirmer derrière. - Décision : titre nomme l'action sensible explicitement (« Valider la campagne Mai 2026 »). Anti-clickjack, anti-mémoire-musculaire. - Décision : bouton primary désactivé jusqu'à 6 chiffres saisis. Cible de Fitts inutile sinon.

─── overlay sombre (--color-modal-backdrop) ───
            ┌────────────────────────────────────────┐
            │  🔐 Confirmer une action sensible       │  H2
            │                                         │
            │  Action : Valider la campagne           │
            │  « Mai 2026 — Plateau »                 │
            │                                         │
            │  Un code à 6 chiffres a été envoyé      │
            │  par SMS au +225 •• •• •• 12 34         │
            │                                         │
            │  [ ][ ][ ][ ][ ][ ]                     │
            │                                         │
            │  Renvoyer le code dans 47 s             │
            │                                         │
            │      [ Annuler ]   [ Confirmer ]        │
            └────────────────────────────────────────┘

Échec 3/3 : modale ferme, banner inline « Action annulée. Réessayez plus tard. » Cancel ou ESC : modale ferme, session opérateur préservée (R-OP-010).

5.7.bis : variante Platform Admin

Même modale, sous-titre : « Action sensible — plateforme ». Pas de nom de cabinet (cross-tenant).

5.8 Bannière d'inactivité (AUTH-OP-06)

Décisions appliquées : - Décision : bannière in-page, non modale, sticky en haut sous la topbar. Le focus reste sur le travail en cours ; si l'utilisateur tape, son input n'est pas perdu — un seul tap sur « Rester connecté(e) » remet le compteur à zéro. - Décision : à expiration → toast persistant + redirect vers /login, MAIS l'input en cours est sauvegardé en draft serveur quand le module concerné le supporte (PRD §B.1 R-OP-011, cf. AUTH-OP-06 AC #2). Si non supporté : warning toast « Votre saisie n'a pas pu être sauvegardée ; reconnectez-vous. »

┌──────────────────────────────────────────────────────────────────────────┐
│  ⏱  Vous serez déconnecté(e) dans 0 min 47 s.       [Rester connecté(e)]│  ← --color-warning-light
└──────────────────────────────────────────────────────────────────────────┘
(contenu de la page normalement visible en dessous)

5.9 Bandeau « Cabinet suspendu » (AUTH-ADM-05, opérateur)

Si pendant la session le cabinet bascule en SUSPENDED (cascade R-OP-014 ≤ 30 s), la prochaine requête côté front renvoie une session-kill ; bandeau plein écran avant redirect /login :

┌──────────────────────────────────────────────────────────────────────────┐
│  ⛔  Cabinet suspendu. Contactez le support Papillon.                     │  --color-error-light
│      Votre session a été fermée pour des raisons de sécurité.            │
│                                                  [Aller à la connexion →]│
└──────────────────────────────────────────────────────────────────────────┘

Sur l'écran /login, toute tentative renvoie copy auth.tenant.suspended.operator. Pas de motif révélé.

5.10 Admin > Utilisateurs — drawer détail (complément op-design §4.12)

Compléments AUTH-spécifiques sur le drawer ouvert depuis la liste opérateurs (le squelette de la liste reste op-design §4.12).

1440 px+
┌──────────────────────────────────────────────────────────────────────────────────────┐
│  Liste utilisateurs                              ┌──────────────────────────────────┐│
│                                                  │  Marie KONÉ                       ││  Drawer
│  …                                               │  [email protected]         ││
│                                                  │  ─────────────────────────────    ││
│                                                  │  Statut       ● Actif              ││
│                                                  │  Rôle         CABINET_ADMIN  [✏]   ││
│                                                  │  Agences      Tenant-wide          ││
│                                                  │  MFA          +225 •• 12 34   [✏] ││
│                                                  │  WhatsApp     ✅ activé             ││
│                                                  │  Dernière     22/05/2026 09:14     ││
│                                                  │  connexion                          ││
│                                                  │                                    ││
│                                                  │  ── Actions admin ──               ││
│                                                  │  [ Émettre un code de secours MFA ]││  Button secondary
│                                                  │  [ Réinitialiser le numéro MFA   ] ││  Button secondary
│                                                  │  [ Désactiver le compte           ]││  Button danger
│                                                  └──────────────────────────────────┘│
└──────────────────────────────────────────────────────────────────────────────────────┘

Chaque action : - « Émettre code de secours » → step-up modal → modale « Code émis » - « Réinitialiser MFA » → step-up modal → input nouveau numéro - « Désactiver » → step-up modal → modale 2 étapes (raison + confirmation) - « Changer rôle » (depuis l'icône ✏) → form inline → step-up modal

5.11 Modale « Code de secours émis » (AUTH-OP-04 / R-OP-007)

Décisions appliquées : - Décision : le code est affiché en gros à l'écran ET envoyé au numéro de l'admin (jamais à l'utilisateur cible directement). L'admin doit le lire à voix haute / le retaper. Friction délibérée. - Décision : countdown live + warning à T-5 min. - Décision : bouton « Copier » bien visible, succès = haptic feedback (toast).

            ┌────────────────────────────────────────┐
            │  ✅ Code de secours émis pour          │
            │     Kouassi BROU                        │
            │                                         │
            │  ┌──────────────────────────────┐       │
            │  │       4 8 2 1 9 7            │       │  --font-size-display
            │  └──────────────────────────────┘       │
            │                  [ Copier ]             │
            │                                         │
            │  ⏱ Valable encore 14 min 58 s.          │
            │                                         │
            │  ⓘ Ce code a aussi été envoyé par SMS  │
            │     à votre numéro (+225 •• •• 22 11). │
            │                                         │
            │  Transmettez-le à l'utilisateur, qui    │
            │  pourra l'utiliser une seule fois à     │
            │  la place du code SMS.                  │
            │                                         │
            │                      [ Fermer ]         │
            └────────────────────────────────────────┘

Audit log : operator_mfa_bypass_issued au moment de la génération (R-OP-007).

5.12 Modale « Désactiver l'utilisateur » (AUTH-OP-07)

Décisions appliquées : - Décision : 2 étapes — choix motif + confirmation explicite. Action destructive (sessions tuées en 60 s, cookies invalidés) ; friction obligatoire. - Décision : motifs au format pill-radio cliquable, pas un dropdown. Tous les motifs visibles immédiatement (paradox of choice contenu, n=4). - Décision : phrase de confirmation finale nomme l'utilisateur (« Désactiver le compte de Kouassi BROU ? »). Anti-erreur de drawer.

Étape 1 — motif :

            ┌────────────────────────────────────────┐
            │  Désactiver Kouassi BROU                │
            │                                         │
            │  Motif (obligatoire) :                  │
            │   ○ Départ volontaire                    │
            │   ○ Licenciement                         │
            │   ○ Fraude suspectée                     │
            │   ○ Autre                                │
            │                                         │
            │   [ Annuler ]    [ Continuer ]          │
            └────────────────────────────────────────┘

Étape 2 — confirm + step-up :

            ┌────────────────────────────────────────┐
            │  ⚠️ Désactiver Kouassi BROU ?           │
            │                                         │
            │  Toutes les sessions ouvertes seront     │
            │  fermées dans la minute. Cette action   │
            │  est consignée dans le journal d'audit. │
            │                                         │
            │  Code SMS de confirmation :             │
            │  [ ][ ][ ][ ][ ][ ]                     │
            │                                         │
            │   [ Annuler ]    [ Désactiver ]         │  Button danger
            └────────────────────────────────────────┘


6. Wireframes — côté [PLATFORM_ADMIN] (delta vs operator)

Aucun écran nouveau. Sur tous les écrans login + MFA + step-up, badge top-bar « Papillon Admin » :

┌───────────────────────────────────────────────┐
│  ▸ Papillon Admin                              │  badge --color-secondary
│                                                │
│  …(form identique à 5.1 / 5.2)…                │
└───────────────────────────────────────────────┘

Step-up cross-tenant : copy = « Action sensible plateforme » au lieu de « cabinet ». La liste des actions sensibles côté PA (TENANT_CREATE / SUSPEND / etc.) est fournie par TENANT-PRD ; AUTH se contente d'imposer le step-up.


7. Spécifications d'interaction — 5 états par écran

Pour CHAQUE écran AUTH ci-dessous, les 5 états canoniques. Le critère « 5 états » est non négociable.

7.1 [C] Landing + Challenge DOB/RCCM

État Affichage Composants
Loading initial (vérification du lien serveur) Header identité (placeholder Skeleton si pas encore résolu) + texte « Vérification du lien… » + Skeleton du card Skeleton (à créer)
Empty (lien valide, jamais tenté) Form challenge tel que 4.1 / 4.9 Card, FormField, Input, Button
Error (saisie fausse n < 5) Border input --color-error, helper inline « Tentative n/5 » avec role=alert, focus reste sur l'input Input error
Offline / low-network Bandeau warning (4.10) + bouton submit montre état loading long, timeout 10 s → toast « Vérifiez votre connexion » Toast, banner custom
Success (DOB OK) Transition douce (200 ms) vers le domaine customer-portal — pas de toast (continue le flow)

États terminaux (lockout / expiré / révoqué / payé / tenant suspendu) sont des écrans à part entière, pas des sous-états de la landing. Chacun a SES PROPRES 5 états : loading = vide (déjà décidé serveur), empty = l'écran lui-même, error = ne s'applique pas, offline = bandeau, success = ne s'applique pas. On documente uniquement loading + offline pour ces écrans terminaux.

7.2 [O] Login étape A (identifiant + mot de passe)

État Affichage
Loading Bouton « Se connecter » en état loading (spinner inline DS)
Empty Form vierge
Error Helper sous le card : « Identifiant ou mot de passe incorrect. » (générique, anti-énumération). Focus sur l'identifiant
Offline / low-network Toast persistant « Connexion lente. Vérifiez votre réseau. » ; bouton reste cliquable (le serveur tranchera)
Success Transition vers étape B (push state, slide)

7.3 [O] Login étape B (MFA OTP)

État Affichage
Loading Bouton « Vérifier » loading; les inputs OTP désactivés
Empty 6 inputs vides, focus sur le 1er
Error OTP invalide → tous les 6 inputs en --color-error 200 ms, vibrent (CSS animation < 300 ms), helper « Code incorrect. Essai n/3. »
Offline Bandeau warning + bouton « Renvoyer » désactivé tant que le réseau est ko
Success Card collapse → redirect dashboard avec toast « Bonjour Marie. » (durée 3 s)

7.4 [O] Step-up modal

Idem 7.3 mais dans une modale. Cancel/ESC = fermeture sans toast. Reset state à la prochaine ouverture.

7.5 [O] Bannière d'inactivité

Pas de variante de loading/empty/error. États : visible (countdown), hidden (utilisateur a cliqué « Rester connecté(e) »), expired (toast + redirect). Reset au prochain événement input/click.

7.6 [A] Admin > Utilisateurs — drawer

Voir op-design §5.7. Compléments AUTH : les actions du drawer (bypass, reset MFA, désactivation) ont chacune leur sous-flow modal (5.11 / 5.12) avec leurs 5 états (loading = step-up en cours, etc.).

7.7 [O] Activation invitation (AUTH-OP-01)

| Loading | Carte placeholder pendant la résolution du token | | Empty | Form vierge (cas nominal) | | Error | Token expiré / consommé / déjà activé → écran dédié (5.5.bis) | | Offline | Bandeau warning ; soumission bloquée car activation = mutation server | | Success | Toast + redirect /login + pré-fill identifiant |

7.8 [O] Mot de passe oublié

Idem login mais avec un copy générique de succès (anti-énumération).


8. Comportement responsive

Customer = mobile-first ; Operator/Admin = desktop-first avec tablette OK ; aucun écran AUTH n'a un sens sur < 320 px.

8.1 Customer

Breakpoint Comportement
360 px (référence) Card pleine largeur moins margins (--space-4), input et bouton 100 % width, identité cabinet sticky-top
414 px Idem 360, espace généreux
768 px (tablette) Card centré max 480 px, marges latérales --space-6 ; identité cabinet centrée
1024 px+ (desktop, rare) Card centré max 480 px ; arrière-plan --color-bg-secondary visible

8.2 Operator / Admin

Breakpoint Comportement
768 (tablette) Card login centré max 420 px, drawer admin = full-screen modal
1024 (desktop base) Card login centré 420 px ; drawer admin = side-drawer largeur 480 px
1440 (dense) Drawer 520 px, liste affiche +1 colonne (last_login)

8.3 Bandeau d'inactivité

Sticky topbar pleine largeur sur tous les breakpoints ; sur mobile (rare en opérateur), il passe au-dessus de la nav.


9. Comportement connectivité ([CUSTOMER])

Customer side = low-bandwidth + resumable, PAS offline-first. Chaque interaction critique est server-side ; un rechargement préserve toujours l'état.

9.1 Slow initial load (3G lent)

  • HTML inline critique pour le header identité (cabinet logo en SVG inline < 5 KB) — l'utilisateur voit son cabinet avant tout JS chargé.
  • Le form challenge fonctionne sans JS (POST natif vers /r/{token}/verify). JS amélior(ait) l'UX (auto-format date, helper inline) mais n'est pas requis.
  • Budget initial < 200 KB gz (AUTH-NFR-01). Pas de Google Fonts (Inter chargé en self-host woff2 sub-set latin).

9.2 Drop mid-flow

  • L'utilisateur tape la DOB puis perd le réseau au submit. La requête timeout à 10 s côté front → toast « Vérifiez votre connexion. Réessayez. » L'input garde sa valeur (pas de reset).
  • Aucun draft côté local storage (la donnée est sensible — DOB). Acceptable : 1 saisie de DOB = pas une douleur même en cas de re-saisie.

9.3 Reload

  • L'URL reste /r/{token}. Reload → le serveur revérifie signature + TTL + révocation + tenant.status, et retourne l'écran approprié (challenge, lockout, expired, …). Compteurs et cycle préservés serveur-side (R-CP-002).
  • Pendant un lockout : le reload affiche le countdown actualisé (« Réessayez dans 24 min »).
  • Pendant une session déjà ouverte (challenge passé) : le reload retourne directement au domaine customer-portal (hors AUTH), via cookie de session lié au token.
  • R-CP-016 : le client peut transférer le lien à son comptable ; les deux devices marchent en parallèle sous le même token. Chaque click est audit-loggé avec son IP / UA.
  • En V1, aucun bandeau « ouvert ailleurs » côté UI (deferred V2). Choix produit : ne pas effrayer un usage légitime (le mandataire de fait).

9.5 Tenant suspended pendant la session customer

  • Si le tenant bascule SUSPENDED pendant que le client est sur le portail post-auth : la prochaine API call renvoie 503 + copy auth.tenant.suspended.customer. Le portail customer (hors AUTH) gère le redirect. AUTH garantit juste que la copy est cohérente.
  • L'ancien lien renvoie écran 4.6 « Un nouveau lien vous a été envoyé ». Pas d'erreur, pas de jargon « token invalidé ».

9.7 Payment-status-unknown au retour du provider

Hors AUTH stricto sensu — relève de CP / PAY. Mais AUTH garantit : un re-click du même lien après une transition payment-pending revoit l'utilisateur dans la même session customer (le token est resumable). AUTH n'invalide rien tant que les conditions R-CP-014 ne sont pas remplies (paiement complet → revoke).


10. Micro-copy française (référentiel)

Toutes les clés auth.* sont définies dans identity-prd.md Appendix. Ce tableau reprend les chaînes EXACTES côté UI + ajoute les chaînes nouvelles introduites par ce doc design.

10.1 Côté Customer

Contexte Chaîne
Header identité, prompt DOB « Bonjour, pour accéder à vos contrats, entrez votre date de naissance. »
Header identité, prompt RCCM « Pour accéder aux contrats de {raisonSociale}, entrez le numéro RCCM de l'entreprise. »
Label DOB « Date de naissance »
Placeholder DOB « JJ / MM / AAAA »
Label RCCM « Numéro RCCM »
Helper RCCM « Insensible à la casse et aux espaces. »
Bouton submit (DOB) « Voir mes contrats »
Bouton submit (RCCM) « Voir nos contrats »
Signal sécurité « 🔒 Connexion sécurisée »
Footer aide « Besoin d'aide ? Appelez votre agence »
Mentions légales « Mentions légales · Politique de confidentialité »
Mention fournisseur « Service fourni par Papillon Collection Solution »
Erreur tentative « La date saisie ne correspond pas. Vérifiez votre date de naissance et réessayez. Tentative {n}/5. »
Erreur dernière chance « ⚠️ Encore une erreur et l'accès sera bloqué 30 minutes. »
Lockout titre « Trop de tentatives »
Lockout corps « Pour des raisons de sécurité, l'accès est mis en pause. Réessayez dans {dureeRestante}. »
Lockout sortie « En cas de doute, appelez votre conseiller : 📞 {agencyPhone} »
Expired titre « Ce lien a expiré »
Expired corps « Contactez votre conseiller pour en recevoir un nouveau : 📞 {agencyPhone} »
Revoked-permanent « Pour des raisons de sécurité, ce lien a été désactivé. Contactez votre conseiller : 📞 {agencyPhone} »
Revoked-reissued « Un nouveau lien vous a été envoyé. Vérifiez vos messages (WhatsApp, SMS ou e-mail). »
Already paid « Tous vos contrats sont en règle. Vous avez déjà payé l'ensemble des contrats de cette échéance. Merci. »
Tenant suspended « Service temporairement indisponible. Veuillez contacter votre cabinet. 📞 {agencyPhone} »
Bandeau low-network « 📶 Connexion lente détectée. Vous pouvez fermer cette page et revenir plus tard, vos tentatives sont conservées. »

10.2 Côté Operator

Contexte Chaîne
Login titre « Connexion »
Bouton login « Se connecter »
Lien forgot « Mot de passe oublié ? »
Login erreur générique « Identifiant ou mot de passe incorrect. »
Compte inactif « Compte non activé. Cliquez sur le lien d'invitation reçu. »
Compte désactivé « Compte désactivé. Contactez votre administrateur. »
Compte verrouillé « Compte verrouillé après plusieurs tentatives. Contactez votre administrateur. »
MFA titre « Code de vérification »
MFA prompt « Un code à 6 chiffres a été envoyé par SMS au {maskedPhone} »
MFA bouton « Vérifier »
MFA check remember « Se souvenir de cet appareil 15 jours »
MFA countdown « Renvoyer le code dans {n} s »
MFA resend link « Renvoyer le code »
MFA WhatsApp banner « 💬 Code envoyé par WhatsApp. Vérifiez votre application. »
MFA no-fallback « ⚠️ Le code n'arrive pas ? Demandez à votre administrateur un code de secours. »
MFA bypass entry button « J'ai un code de secours »
MFA bypass entry title « Code de secours »
MFA bypass entry hint « 6 chiffres, valable 15 minutes, utilisable une seule fois. »
MFA lockée « Vérification verrouillée. Trop de codes incorrects. Réessayez dans {n}. »
OTP erreur « Code incorrect. Essai {n}/3. »
Activation titre « Bienvenue, {displayName} »
Activation pwd rule « Au moins 8 caractères »
Activation pwd label « Choisissez votre mot de passe »
Activation pwd confirm « Confirmez votre mot de passe »
Activation phone label « Numéro pour recevoir les codes (SMS) »
Activation whatsapp check « Ce numéro reçoit aussi WhatsApp »
Activation submit « Activer mon compte »
Activation expired/used « Lien d'invitation expiré ou déjà utilisé. Demandez un nouvel accès à votre administrateur. »
Activation already-active « Votre compte est déjà actif. » + lien « Se connecter → »
Password-reset request titre « Mot de passe oublié »
Password-reset request hint « Entrez votre identifiant. Un lien de réinitialisation vous sera envoyé par e-mail (ou SMS si pas d'e-mail). »
Password-reset request submit « Envoyer le lien »
Password-reset request success « Si un compte existe pour cet identifiant, un lien vient d'être envoyé. Vérifiez vos messages. »
Step-up titre « 🔐 Confirmer une action sensible »
Step-up action prefix « Action : »
Step-up cancel/confirm « Annuler » / « Confirmer »
Step-up aborted toast « Action annulée. Réessayez plus tard. »
Idle warning « Vous serez déconnecté(e) dans {n} s. » + bouton « Rester connecté(e) »
Idle expired toast « Session expirée. Veuillez vous reconnecter. »
Session 8h reached « Session expirée (durée maximale atteinte). Reconnectez-vous. »
Tenant suspended (opérateur) « Cabinet suspendu. Contactez le support Papillon. »
Tenant unavailable (opérateur) « Cabinet indisponible. Contactez le support Papillon. »
Tenant suspended in-session banner « ⛔ Cabinet suspendu. Contactez le support Papillon. Votre session a été fermée pour des raisons de sécurité. »
Bypass émis modal titre « ✅ Code de secours émis pour {displayName} »
Bypass émis modal copy « Transmettez-le à l'utilisateur, qui pourra l'utiliser une seule fois à la place du code SMS. »
Bypass émis ttl « Valable encore {n}. »
Bypass émis notif « ⓘ Ce code a aussi été envoyé par SMS à votre numéro ({maskedAdminPhone}). »
Désactivation titre « Désactiver {displayName} »
Désactivation motif « Motif (obligatoire) : »
Désactivation motifs « Départ volontaire » / « Licenciement » / « Fraude suspectée » / « Autre »
Désactivation confirmation copy « Toutes les sessions ouvertes seront fermées dans la minute. Cette action est consignée dans le journal d'audit. »
Désactivation submit « Désactiver »
Reset MFA submit « Réinitialiser le numéro »
Reset MFA hint « Le nouveau numéro recevra immédiatement le prochain code. »

10.3 Côté Platform Admin

Contexte Chaîne
Top-bar badge « Papillon Admin »
Step-up titre PA « 🔐 Action sensible — plateforme »
Step-up action prefix PA « Action : » (suivi du verbe TENANT_CREATE / SUSPEND / etc. en libellé humain)

11. Spécifications composants

11.1 Composants ré-utilisés du Design System (docs/standards/design-system.md)

Composant Usage AUTH Notes
Card Container central login / activate / forgot / lockout / expired variant default sur web ; pas de padding spacious
Button Toutes CTAs ; variants : primary (Vérifier, Activer, Voir mes contrats) ; secondary (Mot de passe oublié, J'ai un code de secours) ; ghost (Annuler, Retour) ; danger (Désactiver) size=lg sur customer (48 px) ; size=md sur operator
Input DOB, RCCM, mot de passe, identifiant, numéro MFA type=date pour DOB ; type=tel pour MFA phone
FormField Wrapper sur tous les inputs
Checkbox « Se souvenir de cet appareil 15 jours » ; « WhatsApp activé » 48 px touch target
Radio (group) Choix du motif de désactivation description par option non utilisée (radios courts)
Modal Step-up, bypass émis, désactivation 2-step focus trap obligatoire
Toast Validations, erreurs non-bloquantes
BottomSheet (Non utilisé V1 AUTH — réservé customer-portal payment)
Select Non utilisé V1 AUTH

11.2 Composants nouveaux à créer

Composant Variants États Accessibilité
OtpInput size: md / lg ; digits: 6 (fixe V1) default, focus, filled, error (shake animation 200 ms), disabled role=group aria-labelledby ; chaque case aria-label="Chiffre n sur 6" ; auto-advance + back-erase + paste 6 digits ; inputmode="numeric" autocomplete="one-time-code"
IdentityHeader (côté customer) variant: full (logo + nom + tel) / compact (logo + nom) default (hydratés) ; loading (Skeleton) logo en <img alt="{commercialName}">; tel en <a href="tel:…" aria-label="Appeler {commercialName} au {phone}">
LegalFooter (côté customer) default trois liens : Mentions légales / Politique de confidentialité / mention fournisseur ; chaque modale s'ouvre en plein écran sur mobile
Countdown size: sm / md running, expired role="timer" aria-live="off" (mises à jour fréquentes — pas annoncées par lecteur d'écran ; le contexte texte couvre l'info)
IdleWarningBanner (côté operator) hidden, warning (countdown), final sticky top sous topbar ; role="alert" quand visible ; bouton « Rester connecté(e) » volumineux
StepUpModal (composant composé : Modal + OtpInput + countdown) open, verifying, success-close, error-shake, aborted-close hérite focus trap de Modal ; preserve underlying scroll position
Skeleton (déjà priorité MEDIUM dans le DS) placé en placeholder du card landing customer

11.3 Tokens spécifiques

Aucun nouveau token. Les composants ci-dessus consomment les tokens existants (--color-primary, --color-error, --color-warning-light, --space-3/4/6, --shadow-sm/md/lg, --duration-fast/normal).


12. UI de conformité

Statuts mémo v2 : signed-link auth + RBAC = VÉRIFIÉ. Surface d'info Reg. 01-24 sur la landing customer = AUTH-OQ-05 HIGH (à article-ancrer). Opposabilité civile du consentement signé via magic link = INCERTAIN — avocat requis (red flag #5) → toute story qui s'appuie sur le magic link comme preuve de consentement doit porter [LEGAL-REVIEW]. Le design lui est libre de procéder.

12.1 Côté Customer — landing du lien signé

Affichage au-dessus de la ligne de flottaison sur tous les états (AUTH-NFR-10) : - tenant.logo_object_key (en <img alt>) - tenant.commercial_name - agency.phone (cliquable tel:)

Affichage dans le footer, accessible par tap (modale plein écran sur mobile) : - « Mentions légales » → modale contenant tenant.legal_name, tenant.agrement_number, adresse postale (V2), e-mail de contact tenant.support_email. - « Politique de confidentialité » → modale contenant : responsable du traitement (= le cabinet, controller), sous-traitant technique (= Papillon, processor), finalités (renouvellement du contrat, paiement), bases légales (V1 working position « exécution du contrat d'assurance »), durée de conservation, droits ARTCI (accès, rectification, opposition), contact pour les exercer (support_email). À aligner sur AUTH-OQ-05 + AUTH-OQ-06 (LEGAL-REVIEW). - Mention « Service fourni par Papillon Collection Solution » — positionnement sous-traitant technique, CIMA 01-24 R-1 mémo.

12.2 Côté Operator

  • Pas d'obligation Reg. 01-24 spécifique sur la console opérateur (B2B).
  • Mais : footer console doit indiquer « Espace réservé aux collaborateurs habilités. Connexions journalisées. » — déjà couvert op-design.

12.3 Mention dispute

Sur les écrans terminaux customer (lockout / expired / revoked / suspended), la sortie est toujours « Appelez votre conseiller : {agency.phone} ». C'est la procédure de contestation de premier niveau (CIMA 01-24 § information du consommateur). Aucune procédure formelle n'est exposée à ce stade (V1 hors scope) — le téléphone agence est la porte d'entrée.

Les écrans suivants portent le bandeau [LEGAL-REVIEW] jusqu'à résolution de AUTH-OQ-01 + AUTH-OQ-05 + AUTH-OQ-06 : - Landing + challenge DOB (4.1) — s'appuie sur DOB comme facteur de preuve. - Landing + challenge RCCM (4.9) — idem RCCM.

Le design est livré ; la mise en production opposable juridiquement est subordonnée à l'avis de l'avocat (red flag #5 audit mémo v2).


13. Mapping écrans ↔ stories AUTH

Story IDs narratifs du PRD (AUTH-OP-XX, AUTH-CP-XX, AUTH-ADM-XX) + envelope V0 (AUTH-001..006). Le PRODUCT_OWNER renumérotera en AUTH-NNN.

Écran / Surface Story PRD V0 sous-version UI-bearing ? Wireframe runnable
4.1 Landing DOB (challenge) AUTH-CP-02 v0.0.2 (capture des 4 derniers chiffres en V0 ; full DOB = V1) Oui — prioritaire 1
4.2 État erreur tentative AUTH-CP-02 (AC #3) v0.0.2 ✅ (sous-état 4.1) inclus dans prototype 4.1
4.3 Lockout 30 min AUTH-CP-04 / R-CP-010 v0.1.0 (attempt-limit n'est appliqué qu'à v0.1.0) Oui — prioritaire 2
4.4 Lien expiré AUTH-CP-05 v0.1.0 (TTL effective en v0.1.0) inclus dans prototype 4.3 (variantes)
4.5 Lien désactivé sécurité R-CP-013 v0.1.0 inclus dans prototype 4.3
4.6 Lien réémis AUTH-CP-08 / R-CP-014 (b) V1 (re-issuance op-console) inclus dans prototype 4.3
4.7 Déjà payé R-CP-014 (a) V1 inclus dans prototype 4.3
4.8 Cabinet suspendu R-OP-017 / R-CP-020 V1 inclus dans prototype 4.3
4.9 Landing RCCM AUTH-CP-03 v0.0.2 (variante) inclus dans prototype 4.1
4.10 Bandeau low-network (transverse) v0.0.0 inclus dans prototype 4.1
5.1 Login étape A AUTH-OP-02 v0.0.0 (ROPC, sans MFA) Oui — prioritaire 3
5.2 Login étape B (MFA) AUTH-OP-02 / 03 V1 (pas en V0 — MFA reportée V1 PRD §17 actif côté opérateur ; cf. V0 §6 — pas de MFA WhatsApp en V0) inclus dans prototype 5.1
5.3 MFA verrouillée R-OP-006 V1 inclus dans prototype 5.1
5.4 Comptes inactif/désactivé/verrouillé R-OP-008 / 009 v0.0.0 inclus dans prototype 5.1
5.5 Activation invitation AUTH-OP-01 V1 (R-ADM-007 / D-1 dès v0.0.0 pour le bootstrap, mais l'écran activate côté front = V1) Oui — prioritaire 4
5.6 Mot de passe oublié AUTH-ADM-03 V1 (prototype unique optionnel)
5.7 Step-up modal AUTH-OP-05 / R-OP-010 V1 Oui — prioritaire 5
5.8 Idle warning banner AUTH-OP-06 / R-OP-011 V1 inclus dans prototype 5.7
5.9 Tenant-suspended banner (operator in-session) AUTH-ADM-05 V1 inclus dans prototype 5.7
5.10 Drawer admin > utilisateur AUTH-ADM-02 / 04 / 07 V1 (cf. op-design §4.12 pour la liste) inclus dans prototype admin
5.11 Bypass émis modal AUTH-OP-04 V1 inclus dans prototype admin
5.12 Désactivation modale 2 étapes AUTH-OP-07 V1 inclus dans prototype admin
6.x Platform Admin (deltas) AUTH-PA-01 / R-OP-010 platform-admin list V1 ✅ (deltas mineurs sur 5.1 / 5.2 / 5.7) inclus dans prototype 5.1 (toggle PA)

13.1 Stories purement backend (PAS de wireframe)

  • AUTH-CP-01 (link issuance) — back-end pur.
  • AUTH-CP-07 (auto-revocation triggers) — back-end pur.
  • AUTH-OP-08 / 09 (tenant + agency scoping) — back-end pur.
  • AUTH-ADM-04 à 08 (lifecycle handlers) — back-end pur, sauf surfaces déjà couvertes (5.9 affiche le résultat de ADM-05).
  • AUTH-CP-06 (multi-device click) — pas d'UI dédiée V1 (V2 ajoutera un bandeau).
  • AUTH-PA-01 (login platform admin) — surface = écrans operator avec deltas §6.

Annexe A — Décisions UX consignées (synthèse)

# Écran Décision Justification (référence UX)
D1 4.1 Landing 1 écran 1 input vs wizard 2 étapes Hick's Law / engagement déjà acquis par le click
D2 4.1 Landing Header identité sticky, persistant Trust by design / AUTH-NFR-10
D3 4.1 Landing DOB en input type=date + fallback texte Low-bandwidth + Android entrée de gamme
D4 4.1 Landing Pas d'illustration Budget < 200 KB + posture « pas marketing »
D5 4.3 Lockout Countdown live Donne un horizon, cognitive load
D6 4.3 Lockout Pas de bouton « réessayer maintenant » Évite mémoire-musculaire de spam
D7 5.1 Login Card centré 420 px, marque discrète Mapping mental « dialogue », pas « page de marque »
D8 5.2 MFA 6 inputs auto-split vs 1 input long Familier des apps bancaires CIMA
D9 5.2 MFA « Remember device 15j » coché par défaut Sécurité dans le fingerprint, pas l'opt-in
D10 5.2 MFA Countdown resend visible vs bouton grisé Donne un horizon
D11 5.2 MFA Bandeau « WhatsApp » au-dessus du form Évite la surprise sensorielle
D12 5.5 Activate 1 écran (pas wizard) 2 inputs ≠ 2 pages
D13 5.5 Activate Règles pwd avant + checklist live Réduit le rebond submit
D14 5.5 Activate Numéro MFA pré-formaté +225 Smart default
D15 5.7 Step-up Modal, jamais nouvelle page Préserve le contexte de l'action
D16 5.7 Step-up Titre nomme l'action explicitement Anti-clickjack
D17 5.8 Idle Bannière in-page non-modale Préserve focus / input en cours
D18 5.11 Bypass Code affiché à l'écran + envoyé à l'admin Friction délibérée, sécurité
D19 5.12 Désactivation 2 étapes : motif + confirm + step-up Friction proportionnée à l'action destructive
D20 5.12 Désactivation Motifs en pill-radio, tous visibles Paradox of choice contenu (n=4)
D21 6 PA Pas d'écran nouveau ; badge « Papillon Admin » Réutilisation max ; visual marker contre erreur cross-tenant

Annexe B — Prochaines étapes (Phase 3 wireframes)

Cette phase est optionnelle mais recommandée. Pour chaque écran flaggé « UI-bearing » dans §13, on peut invoquer /wireframe identity <STORY-ID> pour produire un prototype React runnable dans docs/wireframes/identity/<STORY-ID>/.

Priorisation suggérée (5 prototypes couvrant toute la surface) :

  1. AUTH-CP-02 — Landing DOB + RCCM (états : initial, error tentative 1..5, low-network bandeau). Inclut toggle dev-only sur les 5 états canoniques.
  2. AUTH-CP-04 — États terminaux customer : lockout, expired, revoked, reissued, paid, tenant-suspended. Toggle dev-only entre les 6 sous-états.
  3. AUTH-OP-02 — Login complet (étape A → étape B → checkbox remember-device → resend SMS → WhatsApp banner → no-fallback → bypass entry → mfa-locked). Toggle dev-only.
  4. AUTH-OP-01 — Activate invitation (états : valide, expiré, déjà consommé, déjà activé).
  5. AUTH-OP-05 — Step-up modal + idle banner + tenant-suspended in-session banner (overlays sur une page d'exemple).

Chaque prototype consomme les tokens de frontend/_shared/tokens.css (jamais de hex en dur), respecte mobile-first 360 px pour les protos customer et desktop-first 1024 px pour les protos operator, et inclut le sélecteur d'état dev-only en haut à droite.


Fin du document — Design Identity (AUTH) V1.