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)¶
- 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).
- 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 ».
- Bannière d'inactivité 30 min — section 5.8 du présent doc, telle quelle (la session timeout n'est pas couplée à MFA).
- É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=nullpour 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 :
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.AUTH-001activate 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¶
- Principes directeurs AUTH
- Architecture d'information & cartes de surfaces
- User Flow Diagrams (Mermaid)
- Wireframes — côté
[CUSTOMER] - Wireframes — côté
[OPERATOR]+[ADMIN] - Wireframes — côté
[PLATFORM_ADMIN](delta vs operator) - Spécifications d'interaction — 5 états par écran
- Comportement responsive
- Comportement connectivité (
[CUSTOMER]slow-3G + drop + reload + link-reuse) - Micro-copy française (référentiel canonique)
- Spécifications composants (ré-utilisation + nouveaux)
- UI de conformité (CIMA 01-24, ARTCI, law 2013-450)
- 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 Admindans 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)¶
3.1 Customer signed-link flow (DOB ou RCCM)¶
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).
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.
9.4 Link reuse (forward, multi-device)¶
- 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.
9.6 Link reuse APRÈS révocation (AUTH-CP-08 reissue)¶
- 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 dansidentity-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.
12.4 Mention [LEGAL-REVIEW] ajouté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 enAUTH-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 dansdocs/wireframes/identity/<STORY-ID>/.
Priorisation suggérée (5 prototypes couvrant toute la surface) :
AUTH-CP-02— Landing DOB + RCCM (états : initial, error tentative 1..5, low-network bandeau). Inclut toggle dev-only sur les 5 états canoniques.AUTH-CP-04— États terminaux customer : lockout, expired, revoked, reissued, paid, tenant-suspended. Toggle dev-only entre les 6 sous-états.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.AUTH-OP-01— Activate invitation (états : valide, expiré, déjà consommé, déjà activé).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.