Aller au contenu

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/init sont stockés uniquement dans un useState de MfaEnrollScreen. Au unmount du composant (navigation arrière ou fin de flux), les valeurs sont purgées automatiquement par React.
  • Interdit : Aucun SecureStore.setItemAsync(), AsyncStorage.setItem(), ou console.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 : RecoveryCodesDisplay reçoit les codes en props. L'état freshCodes est un useState dans useMfa. 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() : ouvre ReauthModal, POST /auth/reauth avec mot de passe.
  • isReauthValid() : vérifie Date.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 : DeleteAccountScreen implé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

  1. 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é.

  2. 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.

  3. QR code TOTP : Aucune librairie QR code n'est actuellement dans le projet. Il faudra ajouter react-native-qrcode-svg ou équivalent. Alternative : le backend fournit un qr_code_data_url (base64 PNG).

  4. 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

  1. 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.

  2. Logout existant : Le logout actuel dans ProfileScreen ne fait que clearAllOnLogout() sans appel serveur. PD-106 enrichit ce comportement (INV-106-13).

  3. Types RootStackParamList : L'ajout de 5 nouvelles routes nécessite une extension propre du typage.

9.3 Pièges

  1. 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.

  2. 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).

  3. 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, SecuritySettingsScreen reste inchangé).
  • Android.
  • Preuve RGPD art. 17 complète (INV-106-18, artefacts backend).
  • Création des endpoints backend (PD séparé à planifier).