PD-106 — Plan d'implémentation¶
1. Découpage en composants¶
1.1 Nouveaux composants¶
| Composant | Type | Responsabilité | Fichier(s) |
|---|---|---|---|
SettingsScreen | Screen | Écran principal Settings : profil lecture seule, point d'entrée unique vers MFA, mot de passe, déconnexion, suppression. Charge le profil et l'état MFA au montage. | src/screens/settings/SettingsScreen.tsx |
MfaSettingsScreen | Screen | Sous-écran MFA : affiche état courant, bascule activation/désactivation, codes de récupération. | src/screens/settings/MfaSettingsScreen.tsx |
MfaEnrollScreen | Screen | Flux d'enrôlement TOTP : QR code, saisie code vérification, affichage codes de récupération. | src/screens/settings/MfaEnrollScreen.tsx |
ChangePasswordScreen | Screen | Formulaire changement mot de passe (ancien, nouveau, confirmation). | src/screens/settings/ChangePasswordScreen.tsx |
DeleteAccountScreen | Screen | Flux suppression compte : avertissement, confirmation renforcée, re-authentification. | src/screens/settings/DeleteAccountScreen.tsx |
ReauthModal | Component | Modal de re-authentification par mot de passe. Gère la validation, le TTL 5 min, l'invalidation sur sortie de flux. | src/components/settings/ReauthModal.tsx |
RecoveryCodesDisplay | Component | Affichage des codes de récupération avec avertissement de criticité. Gère l'état "affichage unique". | src/components/settings/RecoveryCodesDisplay.tsx |
ConfirmationModal | Component | Modal de confirmation explicite (simple ou renforcée). Réutilisable pour désactivation MFA, déconnexion, suppression. | src/components/settings/ConfirmationModal.tsx |
useMfa | Hook | Logique métier MFA : état, activation, désactivation, codes de récupération. Appels API + gestion d'état. | src/hooks/useMfa.ts |
useReauth | Hook | Gestion de la re-authentification : demande, validation, TTL 5 min, invalidation par navigation. | src/hooks/useReauth.ts |
useSettings | Hook | Chargement du profil utilisateur et état MFA serveur. | src/hooks/useSettings.ts |
settingsApi | Service | Couche API pour les endpoints PD-106 : profil, MFA, mot de passe, logout, suppression. | src/services/settingsApi.ts |
mfa namespace i18n | i18n | Traductions FR/EN pour les écrans MFA et Settings PD-106. | src/i18n/locales/{fr,en}/mfa.json |
settings namespace i18n | i18n | Traductions FR/EN pour l'écran Settings principal. | src/i18n/locales/{fr,en}/settings.json |
1.2 Composants existants modifiés¶
| Composant | Modification |
|---|---|
AppNavigator.tsx | Ajout des 5 nouvelles routes (Settings, MfaSettings, MfaEnroll, ChangePassword, DeleteAccount). |
ProfileScreen.tsx | Remplacer le lien "Security" par un lien vers le nouvel écran Settings. Conserver comme écran de profil accessible depuis Settings. |
ProfileAvatar | Modifier onPress pour naviguer vers Settings au lieu de Profile. |
src/services/api.ts | Ajouter les nouveaux endpoints PD-106 (MFA, password, delete account). |
src/hooks/useAuth.ts | Enrichir logout() pour appeler l'invalidation session serveur (INV-106-13). |
src/i18n/index.ts | Enregistrer les namespaces mfa et settings. |
1.3 Architecture navigation PD-106¶
Home (headerRight: ProfileAvatar → Settings)
└─ Settings (profil + menu)
├─ MfaSettings (état MFA + actions)
│ └─ MfaEnroll (flux activation TOTP)
├─ ChangePassword (formulaire)
├─ SecuritySettings (existant PD-174, auto-lock/biométrie)
└─ DeleteAccount (flux suppression)
La navigation vers un écran hors du parcours Settings invalide la re-authentification en cours (INV-106-05, TC-NOM-12).
2. Flux techniques¶
2.1 F-106-01 — Accès Settings et consultation profil¶
1. Utilisateur tape sur ProfileAvatar → navigate("Settings")
2. SettingsScreen.mount()
→ useSettings().loadProfile() → GET /user/profile → {name, email, avatar?}
→ useSettings().loadMfaState() → GET /user/mfa/status → {active, method}
3. Si loadMfaState() échoue → état "inconnu", actions MFA bloquées
4. Render: profil (nom, email, avatar/initiales) en lecture seule + menu
2.2 F-106-02 — Activation MFA TOTP¶
1. SettingsScreen → MfaSettings (état MFA affiché)
2. Utilisateur appuie "Activer MFA"
3. ReauthModal s'ouvre → saisie mot de passe
→ POST /auth/reauth → validation serveur → timestamp re-auth mémorisé
4. Si re-auth OK → navigate("MfaEnroll")
5. MfaEnrollScreen.mount()
→ POST /user/mfa/totp/init → {secret, otpauth_uri, qr_code_data_url}
→ Afficher QR code (Image base64) + secret texte
6. Utilisateur saisit code TOTP dans TextInput
7. POST /user/mfa/totp/verify {code} → 200 {recovery_codes: string[]}
→ Si succès: afficher RecoveryCodesDisplay (affichage unique, avertissement)
→ Reload état MFA serveur
→ Si échec: ERR-106-MFA-CODE-INVALID, nouvelle tentative
8. Quitter MfaEnrollScreen → recovery_codes purgés de la mémoire React
2.3 F-106-03 — Consultation / Régénération codes de récupération¶
1. MfaSettingsScreen → section "Codes de récupération"
2. Si pas d'affichage unique en cours → message "régénération nécessaire"
3. Utilisateur appuie "Régénérer"
→ ConfirmationModal (confirmation explicite)
→ ReauthModal (re-authentification)
4. POST /user/mfa/recovery/regenerate → {recovery_codes: string[]}
→ Si succès: RecoveryCodesDisplay (affichage unique)
→ Anciens codes invalidés (serveur)
5. Fermeture RecoveryCodesDisplay → codes purgés de la mémoire
2.4 F-106-04 — Désactivation MFA¶
1. MfaSettingsScreen → "Désactiver MFA"
2. ConfirmationModal (confirmation explicite)
3. ReauthModal (re-authentification)
4. POST /user/mfa/disable → 200
→ Si succès: état MFA → "désactivé", reload serveur
→ Si échec: ERR-106-MFA-DISABLE-DENIED
2.5 F-106-05 — Changement de mot de passe¶
1. Settings → navigate("ChangePassword")
2. Formulaire: ancien mdp, nouveau mdp, confirmation
3. Validation locale: confirmation === nouveau (sinon ERR-106-PWD-CONFIRM-MISMATCH)
4. ReauthModal → re-authentification
5. POST /user/password/change {old_password, new_password}
→ Si succès: session invalidée, clearAllOnLogout(), navigate("Login")
→ Si échec: ERR-106-PWD-POLICY avec motif serveur exploitable
2.6 F-106-06 — Déconnexion¶
1. Settings → "Se déconnecter"
2. ConfirmationModal (confirmation explicite)
3. POST /auth/logout → invalidation session serveur
→ Si succès: clearAllOnLogout(), navigation.reset → Login
→ Si échec réseau: ERR-106-LOGOUT-FAILED
→ clearAllOnLogout() quand même (fallback dégradation gracieuse)
→ navigation.reset → Login
→ Message explicite (risque session zombie)
2.7 F-106-07 — Suppression de compte¶
1. Settings → navigate("DeleteAccount")
2. Avertissement d'irréversibilité
3. Confirmation renforcée (étape 1 : saisir "SUPPRIMER" + bouton)
4. Confirmation finale (étape 2 : dialog "Êtes-vous sûr ?")
5. ReauthModal → re-authentification
6. DELETE /user/account → 200
→ Si succès: clearAllOnLogout(), navigation.reset → Login
→ Si échec: ERR-106-DELETE-FAILED, compte non supprimé
2bis. Diagrammes Mermaid¶
2bis.1 Graphe de dépendances des composants¶
graph TD
subgraph Screens
SS[SettingsScreen]
MFS[MfaSettingsScreen]
MES[MfaEnrollScreen]
CPS[ChangePasswordScreen]
DAS[DeleteAccountScreen]
end
subgraph Components
RM[ReauthModal]
RCD[RecoveryCodesDisplay]
CM[ConfirmationModal]
end
subgraph Hooks
uM[useMfa]
uR[useReauth]
uS[useSettings]
uA[useAuth]
end
subgraph Services
sAPI[settingsApi]
end
subgraph Navigation
AN[AppNavigator]
PA[ProfileAvatar]
end
subgraph i18n
I18N_MFA[mfa.json]
I18N_SET[settings.json]
end
PA -->|navigate| AN
AN --> SS
AN --> MFS
AN --> MES
AN --> CPS
AN --> DAS
SS --> uS
SS --> uR
SS --> RM
SS --> CM
MFS --> uM
MFS --> uR
MFS --> RM
MFS --> CM
MFS --> RCD
MES --> uM
MES --> RCD
CPS --> uR
CPS --> RM
DAS --> uR
DAS --> RM
DAS --> CM
uM --> sAPI
uS --> sAPI
uR --> sAPI
uA --> sAPI
SS --> I18N_SET
MFS --> I18N_MFA
MES --> I18N_MFA
CPS --> I18N_SET
DAS --> I18N_SET 2bis.2 Diagramme de séquence — Activation MFA TOTP (F-106-02)¶
sequenceDiagram
actor U as Utilisateur
participant SS as SettingsScreen
participant MFS as MfaSettingsScreen
participant RM as ReauthModal
participant uR as useReauth
participant MES as MfaEnrollScreen
participant uM as useMfa
participant API as settingsApi
participant BE as Backend
U->>SS: Tap "MFA"
SS->>MFS: navigate("MfaSettings")
U->>MFS: Tap "Activer MFA"
MFS->>RM: Ouvrir modal re-auth
U->>RM: Saisie mot de passe
RM->>uR: requireReauth(password)
uR->>API: POST /auth/reauth
API->>BE: Vérification mot de passe
BE-->>API: 200 OK
API-->>uR: Re-auth validée
uR-->>RM: Succès (timestamp mémorisé)
RM-->>MFS: Fermer modal
MFS->>MES: navigate("MfaEnroll")
MES->>uM: initTotp()
uM->>API: POST /user/mfa/totp/init
API->>BE: Génération secret TOTP
BE-->>API: {secret, otpauth_uri, qr_code_data_url}
API-->>uM: Données TOTP
uM-->>MES: Afficher QR code + secret
U->>MES: Saisie code TOTP
MES->>uM: verifyTotp(code)
uM->>API: POST /user/mfa/totp/verify {code}
API->>BE: Vérification code
BE-->>API: 200 {recovery_codes}
API-->>uM: Codes de récupération
uM-->>MES: Afficher RecoveryCodesDisplay
Note over MES: Codes en useState uniquement<br/>Purgés au unmount (INV-106-08)
U->>MES: Fermer / Retour
MES->>MES: unmount → purge secrets mémoire 2bis.3 Diagramme de séquence — Suppression de compte (F-106-07)¶
sequenceDiagram
actor U as Utilisateur
participant SS as SettingsScreen
participant DAS as DeleteAccountScreen
participant CM as ConfirmationModal
participant RM as ReauthModal
participant uR as useReauth
participant uA as useAuth
participant API as settingsApi
participant BE as Backend
U->>SS: Tap "Supprimer mon compte"
SS->>DAS: navigate("DeleteAccount")
DAS->>DAS: Afficher avertissement irréversibilité
U->>DAS: Saisir "SUPPRIMER" (étape 1)
DAS->>CM: Confirmation finale (étape 2)
U->>CM: Confirmer
CM-->>DAS: Confirmé
DAS->>RM: Ouvrir modal re-auth
U->>RM: Saisie mot de passe
RM->>uR: requireReauth(password)
uR->>API: POST /auth/reauth
API->>BE: Vérification
BE-->>API: 200 OK
API-->>uR: Re-auth validée
uR-->>RM: Succès
RM-->>DAS: Fermer modal
DAS->>uR: consume()
DAS->>API: DELETE /user/account
API->>BE: Suppression compte
BE-->>API: 200 OK
API-->>DAS: Succès
DAS->>uA: clearAllOnLogout()
Note over uA: SecureStore + AsyncStorage<br/>+ Zustand vidés (INV-106-13)
DAS->>DAS: navigation.reset → Login 3. Mapping invariants → mécanismes¶
| Invariant ID | Exigence | Mécanisme | Composant | Observable | Risque |
|---|---|---|---|---|---|
| INV-106-01 | Point d'entrée unique Settings | SettingsScreen comme écran hub ; ProfileAvatar.onPress → navigate("Settings") | AppNavigator, ProfileAvatar, SettingsScreen | Navigation unique depuis header vers Settings | Faible |
| INV-106-02 | Profil lecture seule (nom, email, avatar/initiales) | useSettings().loadProfile() → render <Text> sans champs éditables | SettingsScreen | Inspection UI : aucun TextInput, aucun bouton d'édition | Faible |
| INV-106-03 | Compte identifiable sans ambiguïté | Email affiché en clair dans le header de SettingsScreen | SettingsScreen | Présence d'un identifiant lisible | Faible |
| INV-106-04 | État MFA lu au chargement + après mutation ; fallback "inconnu" | useSettings().loadMfaState() au mount + après chaque mutation ; catch → "unknown" | useSettings, useMfa | Trace appel GET /user/mfa/status + UI "inconnu" si erreur | Moyen : dépend de la fiabilité réseau |
| INV-106-05 | Re-auth avant chaque opération critique | useReauth() avec TTL 5 min ; invalidation sur navigation hors flux | useReauth, ReauthModal | Modal re-auth systématique ; opération bloquée sans re-auth validée | Élevé : point central de sécurité |
| INV-106-06 | Re-auth non réutilisable entre opérations | useReauth().consume() après chaque opération critique ; compteur consommation | useReauth | Tentative B après A sans nouvelle re-auth → refus | Élevé |
| INV-106-07 | Activation MFA après confirmation serveur uniquement | POST /user/mfa/totp/verify → 200 avant tout changement d'état UI | useMfa, MfaEnrollScreen | État MFA inchangé tant que 200 non reçu | Moyen |
| INV-106-08 | Secrets TOTP et codes non persistés localement | Secrets uniquement en état React (useState) ; nettoyage au unmount/navigation ; aucun SecureStore/AsyncStorage | MfaEnrollScreen, RecoveryCodesDisplay | Audit storage post-flux : absence de sentinelles | Élevé : audit explicite requis |
| INV-106-09 | Codes affichés une seule fois + avertissement | RecoveryCodesDisplay conditionné par freshCodes (état éphémère) ; avertissement obligatoire | RecoveryCodesDisplay, useMfa | Codes masqués si fermeture + réouverture ; avertissement visible | Moyen |
| INV-106-10 | Régénération invalide les anciens codes | Délégué au serveur (H-106-04) ; mobile observe le comportement | useMfa | Ancien code refusé après régénération | Faible (dépend backend) |
| INV-106-11 | Désactivation exige confirmation + re-auth | ConfirmationModal puis ReauthModal séquentiels | MfaSettingsScreen | Tentatives partielles refusées | Moyen |
| INV-106-12 | Changement mdp : ancien + nouveau + confirmation + motif exploitable | Validation locale mismatch + rendu erreur serveur | ChangePasswordScreen | Mismatch → refus local ; erreur serveur affichée | Faible |
| INV-106-13 | Logout invalide session serveur + efface local ; fallback si échec | POST /auth/logout + clearAllOnLogout() ; fallback : nettoyage local forcé + message | useAuth (enrichi), SettingsScreen | Session invalide côté serveur ; stockage local vidé | Moyen |
| INV-106-14 | Suppression : irréversibilité + confirmation renforcée + re-auth | DeleteAccountScreen avec 3 étapes séquentielles | DeleteAccountScreen | Étapes incomplètes → suppression non exécutée | Élevé |
| INV-106-15 | Après suppression : déconnexion + compte inutilisable | DELETE /user/account + clearAllOnLogout + navigation Login | DeleteAccountScreen | Tentative login ultérieure échoue | Faible (dépend backend) |
| INV-106-16 | Aucun échec critique silencieux | Pattern uniforme try/catch → Alert.alert(t("error.code")) dans chaque opération | Tous les écrans/hooks | Erreur visible pour chaque opération | Moyen |
| INV-106-17 | Textes localisés selon langue active | Namespaces i18n mfa et settings ; useTranslation() partout | i18n locales | Changement langue → textes traduits | Faible |
| INV-106-18 | RGPD art.17 hors périmètre mobile | Non implémenté côté mobile | — | Artefacts backend/compliance | — |
4. Mapping critères d'acceptation → mécanismes¶
| Critère ID | Mécanisme(s) | Composant | Observable | Risque |
|---|---|---|---|---|
| CA-106-01 | SettingsScreen comme point d'entrée unique | AppNavigator, SettingsScreen | Profil + MFA + mdp + logout + suppression accessibles depuis Settings | Faible |
| CA-106-02 | useSettings().profile rendu en <Text> | SettingsScreen | Nom, email, avatar/initiales visibles, non éditables | Faible |
| CA-106-03 | Email affiché dans header Settings | SettingsScreen | Email lisible | Faible |
| CA-106-04 | loadMfaState() au mount + après mutation ; catch → "inconnu" | useSettings | Traces GET + UI "inconnu" si erreur | Moyen |
| CA-106-05 | useReauth() obligatoire avant mutation critique | useReauth | Opération refusée sans re-auth | Élevé |
| CA-106-06 | Activation conditionée par 200 de verify | useMfa | MFA inchangé si verify échoue | Moyen |
| CA-106-07 | Alert.alert() avec code erreur | useMfa | Message visible + état MFA inchangé | Faible |
| CA-106-08 | RecoveryCodesDisplay conditionnel + avertissement | RecoveryCodesDisplay | Codes absents hors affichage unique ; avertissement visible | Moyen |
| CA-106-09 | Serveur invalide anciens codes (H-106-04) | useMfa | Ancien code refusé post-régénération | Faible |
| CA-106-10 | Séquence ConfirmationModal → ReauthModal | MfaSettingsScreen | Tentatives partielles refusées | Moyen |
| CA-106-11 | Validation locale newPwd !== confirmPwd | ChangePasswordScreen | Refus immédiat, pas d'appel serveur | Faible |
| CA-106-12 | Affichage error.message du serveur | ChangePasswordScreen | Message exploitable visible | Faible |
| CA-106-13 | POST /auth/logout + vérification session invalidée | useAuth | Token réutilisé → refus serveur | Moyen |
| CA-106-14 | clearAllOnLogout() après logout | useAuth | SecureStore/AsyncStorage vidés | Faible |
| CA-106-15 | DeleteAccountScreen avec 3 étapes séquentielles | DeleteAccountScreen | Étapes incomplètes → pas de suppression | Élevé |
| CA-106-16 | DELETE /user/account + clearAll + Login | DeleteAccountScreen | Login ultérieur échoue | Faible |
| CA-106-17 | Secrets uniquement en useState ; nettoyage unmount | MfaEnrollScreen, RecoveryCodesDisplay | Audit storage : absence secrets | Élevé |
| CA-106-18 | Namespaces i18n mfa + settings | i18n | Textes traduits | Faible |
| CA-106-19 | Hors périmètre mobile | — | — | — |
5. Mapping tests (TC-*) → mécanismes + observables¶
| Test ID | Référence spec | Mécanisme(s) | Point(s) d'observation | Niveau de test visé |
|---|---|---|---|---|
| TC-NOM-01 | F-106-01, INV-01/02/03/04 | useSettings + SettingsScreen render | Navigation Settings → UI profil + état MFA | Integration |
| TC-NOM-02 | F-106-02, INV-05/07/08/09 | useMfa.activate + MfaEnrollScreen | Flux complet activation → codes affichés | Integration |
| TC-NOM-03 | INV-05/06 | useReauth.consume + 2 opérations séquentielles | 2ème opération refusée sans nouvelle re-auth | Unit + Integration |
| TC-NOM-04 | F-106-03, INV-05/09/10 | useMfa.regenerateRecoveryCodes | Nouveau jeu affiché, ancien invalide | Integration |
| TC-NOM-05 | F-106-04, INV-11 | useMfa.disable + ConfirmationModal + ReauthModal | Séquence complète → MFA désactivé | Integration |
| TC-NOM-06 | F-106-05, INV-05/12 | ChangePasswordScreen + useAuth.rotatePassword | Changement réussi → session invalidée → Login | Integration |
| TC-NOM-07 | F-106-06, INV-13 | useAuth.logout enrichi | Session serveur invalidée + local nettoyé | Integration |
| TC-NOM-08 | F-106-07, INV-14/15 | DeleteAccountScreen flux complet | Confirmation renforcée (2 étapes) + re-auth + suppression | Integration |
| TC-NOM-09 | INV-17 | i18n namespaces mfa + settings | Bascule FR↔EN → textes traduits | Unit |
| TC-NOM-10 | F-106-03, INV-09 | RecoveryCodesDisplay absence hors contexte | Pas de codes en clair si pas de génération récente | Unit |
| TC-NOM-11 | INV-05 | useReauth TTL 5 min | Re-auth > 5 min → rejet opération | Unit |
| TC-NOM-12 | INV-05 | useReauth invalidation navigation | Sortie flux → re-auth invalidée | Unit + Integration |
| TC-ERR-01 | ERR-SESSION-EXPIRED | Intercepteur API 401 → clearAll + Login | Navigation forcée Login + message | Integration |
| TC-ERR-02 | ERR-NETWORK | catch réseau → Alert.alert | Message explicite, pas de faux succès | Unit |
| TC-ERR-03 | ERR-MFA-STATE-UNAVAILABLE | loadMfaState catch → "inconnu" + actions bloquées | UI "inconnu" + boutons MFA disabled | Unit |
| TC-ERR-04 | ERR-MFA-ENROLL-FAILED | totp/init catch → message + pas de secret local | MFA reste désactivé | Unit |
| TC-ERR-05 | ERR-MFA-CODE-INVALID | totp/verify 400 → message + retry | Pas de succès affiché | Unit |
| TC-ERR-06 | ERR-MFA-DISABLE-DENIED | disable 403 → message + MFA reste actif | État MFA inchangé | Unit |
| TC-ERR-07 | ERR-RECOVERY-NOT-AVAILABLE | RecoveryCodesDisplay conditionnel | Aucun code en clair + message régénération | Unit |
| TC-ERR-08 | ERR-RECOVERY-REGEN-FAILED | regenerate catch → message + anciens codes valides | Anciens codes préservés | Unit |
| TC-ERR-09 | ERR-PWD-CONFIRM-MISMATCH | Validation locale !== → refus | Pas d'appel API | Unit |
| TC-ERR-10 | ERR-PWD-POLICY | change-password 400 → motif serveur | Message exploitable | Unit |
| TC-ERR-11 | ERR-LOGOUT-FAILED | logout catch → clearAll local forcé + message | Nettoyage local + état non-auth + message zombie | Integration |
| TC-ERR-12 | ERR-DELETE-CONFIRMATION | Confirmation renforcée invalide → blocage | Suppression non exécutée | Unit |
| TC-ERR-13 | ERR-DELETE-REAUTH | Re-auth échouée → blocage | Suppression non exécutée | Unit |
| TC-ERR-14 | ERR-DELETE-FAILED | DELETE 500 → message + compte actif | Pas de faux succès | Unit |
| TC-ERR-15 | ERR-RATE-LIMIT | 429 → message avec contrainte temporelle | Message exploitable | Unit |
| TC-INV-01 | INV-08 | Audit SecureStore/AsyncStorage post-flux | Absence de sentinelles secret/codes | E2E/Sec |
| TC-INV-02 | INV-16 | Chaque erreur → Alert.alert | Aucune erreur silencieuse | Integration |
| TC-INV-03 | INV-18 | Non testable mobile | Artefacts backend | — |
6. Gestion des erreurs¶
6.1 Pattern uniforme¶
Chaque opération critique suit le pattern :
try {
// Appel API
const result = await settingsApi.operation();
if (!result.success) {
Alert.alert(t(`errors.${result.code}`), result.error);
return;
}
// Succès
} catch (error) {
// Erreur réseau/inattendue
Alert.alert(t("errors.network"), t("errors.networkMessage"));
}
6.2 Mapping codes erreur → comportement¶
| Code erreur | HTTP | Comportement mobile | Traduction i18n |
|---|---|---|---|
| ERR-106-SESSION-EXPIRED | 401 | clearAllOnLogout() + navigate Login + message | errors.sessionExpired |
| ERR-106-NETWORK | — | Message explicite, aucun changement d'état | errors.network |
| ERR-106-MFA-STATE-UNAVAILABLE | 500/timeout | État "inconnu", actions MFA disabled | errors.mfaStateUnavailable |
| ERR-106-MFA-ENROLL-FAILED | 400/500 | Message, MFA reste désactivé, pas de secret local | errors.mfaEnrollFailed |
| ERR-106-MFA-CODE-INVALID | 400 | Message, nouvelle tentative possible | errors.mfaCodeInvalid |
| ERR-106-MFA-DISABLE-DENIED | 403 | Message, MFA reste actif | errors.mfaDisableDenied |
| ERR-106-RECOVERY-NOT-AVAILABLE | — | Pas de codes affichés, message régénération | errors.recoveryNotAvailable |
| ERR-106-RECOVERY-REGEN-FAILED | 500 | Message, anciens codes préservés | errors.recoveryRegenFailed |
| ERR-106-PWD-CONFIRM-MISMATCH | — | Refus local immédiat | errors.passwordMismatch |
| ERR-106-PWD-POLICY | 400 | Motif serveur affiché | errors.passwordPolicy |
| ERR-106-LOGOUT-FAILED | 500/timeout | clearAllOnLogout() forcé + message zombie | errors.logoutFailed |
| ERR-106-DELETE-CONFIRMATION | — | Blocage local | errors.deleteConfirmation |
| ERR-106-DELETE-REAUTH | 403 | Blocage, message | errors.deleteReauth |
| ERR-106-DELETE-FAILED | 500 | Message, compte non supprimé | errors.deleteFailed |
| ERR-106-RATE-LIMIT | 429 | Message avec contrainte temporelle si fournie | errors.rateLimit |
6.3 Intercepteur 401 global¶
L'intercepteur API existant dans api.ts sera enrichi : si un appel retourne 401 sur les endpoints PD-106, déclencher clearAllOnLogout() + navigation vers Login avec message ERR-106-SESSION-EXPIRED.
7. Impacts sécurité¶
7.1 Secrets TOTP (INV-106-08)¶
- Mécanisme : Le secret TOTP et l'URI otpauth reçus de
/user/mfa/totp/initsont stockés uniquement dans unuseStatedeMfaEnrollScreen. Auunmountdu composant (navigation arrière ou fin de flux), les valeurs sont purgées automatiquement par React. - Interdit : Aucun
SecureStore.setItemAsync(),AsyncStorage.setItem(), ouconsole.log()ne doit contenir le secret TOTP ou les codes de récupération. - Vérification : TC-INV-01 avec valeurs sentinelles.
7.2 Codes de récupération (INV-106-09)¶
- Mécanisme :
RecoveryCodesDisplayreçoit les codes en props. L'étatfreshCodesest unuseStatedansuseMfa. Toute navigation hors composant purge cet état. Aucune persistance. - Avertissement : Texte de criticité obligatoire affiché au-dessus des codes.
7.3 Re-authentification (INV-106-05, INV-106-06)¶
- Mécanisme :
useReauth()expose{requireReauth, isReauthValid, consume}. requireReauth(): ouvreReauthModal, POST/auth/reauthavec mot de passe.isReauthValid(): vérifieDate.now() - reauthTimestamp < 5 * 60 * 1000.consume(): invalide la re-auth après utilisation (one-time).- Listener navigation :
navigation.addListener("blur")sur les écrans Settings invalide la re-auth. - Risque : Le TTL est évalué côté client (contrainte temporelle évaluée par le serveur selon spec). Le serveur re-vérifie au moment de l'opération critique.
7.4 Nettoyage local (INV-106-13)¶
- Mécanisme :
clearAllOnLogout()existant (PD-174) efface SecureStore + AsyncStorage + mémoire Zustand. Réutilisé pour logout, suppression compte, changement mdp. - Extension : Vérifier que les tokens JWT (accessToken, refreshToken) sont bien dans le périmètre de
clearAllOnLogout().
7.5 Confirmation renforcée (INV-106-14)¶
- Mécanisme :
DeleteAccountScreenimplémente 2 étapes séquentielles : - Saisie du texte "SUPPRIMER" (ou "DELETE" en anglais) dans un TextInput.
- Dialog de confirmation finale (Alert.alert avec bouton destructif).
- Les 2 étapes doivent être validées dans le même flux ; toute sortie réinitialise le processus.
7.6 Journalisation¶
- Aucun secret (TOTP, codes de récupération, mot de passe) ne doit apparaître dans
console.log(),console.error(), ou tout autre mécanisme de log. - Les logs d'erreur ne contiennent que le code erreur, pas le payload.
8. Hypothèses techniques¶
| ID | Hypothèse | Impact si faux |
|---|---|---|
| HT-106-01 | Le backend expose GET /user/profile retournant {name, email, avatar_url?}. | Profil incomplet ; fallback initiales. |
| HT-106-02 | Le backend expose GET /user/mfa/status retournant {active: boolean, method: string}. | Impossible d'afficher l'état MFA fiable. |
| HT-106-03 | Le backend expose POST /user/mfa/totp/init retournant {secret, otpauth_uri, qr_code_data_url?}. | Activation MFA impossible. |
| HT-106-04 | Le backend expose POST /user/mfa/totp/verify avec {code} retournant {recovery_codes: string[]}. | Pas de codes de récupération à l'activation. |
| HT-106-05 | Le backend expose POST /user/mfa/disable. | Désactivation MFA impossible. |
| HT-106-06 | Le backend expose POST /user/mfa/recovery/regenerate retournant {recovery_codes: string[]}. | Régénération impossible. |
| HT-106-07 | Le backend expose POST /auth/reauth avec {password} retournant un token/timestamp validant la re-auth. | Mécanisme de re-auth non fonctionnel. |
| HT-106-08 | Le backend expose POST /user/password/change avec {old_password, new_password} et retourne des erreurs qualifiées. | Messages non exploitables. |
| HT-106-09 | Le backend expose DELETE /user/account protégé par SensitiveActionGuard. | Suppression impossible. |
| HT-106-10 | Le backend expose POST /auth/logout invalidant effectivement la session serveur. | CA-106-13 non atteignable. |
| HT-106-11 | L'API retourne un code HTTP 429 avec payload {retry_after?: number} pour le rate-limit. | Message temporel incomplet. |
| HT-106-12 | Le QR code TOTP peut être affiché via une Image base64 ou une URI otpauth via une librairie de QR code React Native. | Nécessité d'ajouter une dépendance QR code (ex: react-native-qrcode-svg). |
9. Points de vigilance (risques, dette, pièges)¶
9.1 Risques¶
-
Endpoints backend non encore implémentés : La plupart des endpoints PD-106 (MFA TOTP init/verify/disable, recovery codes, reauth, password change, delete account) ne sont pas visibles dans le codebase backend actuel. Le middleware MFA existe mais les routes d'administration MFA sont absentes. Il faudra coordonner avec l'équipe backend ou créer un PD dédié.
-
Re-auth côté client vs serveur : Le TTL 5 min est vérifié côté client ET côté serveur (spec : "cette borne temporelle est évaluée par le serveur"). Si le serveur re-valide indépendamment, le client peut afficher un refus tardif. Prévoir un message exploitable pour ce cas.
-
QR code TOTP : Aucune librairie QR code n'est actuellement dans le projet. Il faudra ajouter
react-native-qrcode-svgou équivalent. Alternative : le backend fournit unqr_code_data_url(base64 PNG). -
Navigation invalidation : L'invalidation de re-auth sur navigation hors flux repose sur
navigation.addListener("blur"). Ce mécanisme doit couvrir le cas où l'utilisateur utilise le swipe-back iOS ou le bouton hardware.
9.2 Dette technique¶
-
ProfileScreen existant : L'écran actuel affiche un profil statique (
t("defaultUserName")). PD-106 le remplace par des données serveur réelles. La transition doit être propre. -
Logout existant : Le logout actuel dans
ProfileScreenne fait queclearAllOnLogout()sans appel serveur. PD-106 enrichit ce comportement (INV-106-13). -
Types RootStackParamList : L'ajout de 5 nouvelles routes nécessite une extension propre du typage.
9.3 Pièges¶
-
Affichage unique codes : Ne pas utiliser un state global (Zustand) pour les codes de récupération — cela créerait une persistance mémoire au-delà du composant.
-
React Navigation state : Lors d'un
navigation.reset()après logout/suppression, vérifier que les écrans Settings sont bien dépilés (pas de fuite de composant avec secrets en mémoire). -
Double confirmation renforcée : La spec exige "validation initiale puis confirmation finale" (TC-NOM-08). Ce sont bien 2 étapes distinctes, pas 2 clics sur le même bouton.
10. Hors périmètre¶
- Modification des endpoints backend existants (H-106-01 à H-106-10 : les endpoints sont supposés disponibles).
- Nouvelles méthodes MFA (SMS, email, WebAuthn).
- Flux de login MFA (saisie TOTP à la connexion).
- Édition du profil (nom, email, avatar).
- Biométrie et auto-lock (déjà gérés par PD-174,
SecuritySettingsScreenreste inchangé). - Android.
- Preuve RGPD art. 17 complète (INV-106-18, artefacts backend).
- Création des endpoints backend (PD séparé à planifier).