Aller au contenu

Expression de Besoin — PD-238

Endpoints de gestion MFA utilisateur


1. Contexte

ProbatioVault dispose d'un système de validation MFA délégué à l'IdP Keycloak (PD-27), qui vérifie si un utilisateur est authentifié avec MFA via les claims OIDC (amr, acr). Cependant, aucun endpoint ne permet à un utilisateur de gérer ses paramètres MFA (activer/désactiver TOTP, consulter l'état MFA, gérer les codes de récupération).

PD-238 couvre uniquement les endpoints de gestion MFA. Les endpoints de changement de mot de passe, suppression de compte et logout relèvent d'autres user stories.

Relation avec PD-106 (consommateur principal)

L'application mobile iOS PD-106 (Écran Settings iOS) est le principal consommateur de PD-238. PD-106 a été spécifiée avec l'hypothèse que des endpoints backend de gestion MFA existent :

Endpoint attendu par PD-106 Fonction
GET /user/mfa/status État MFA (actif/inactif, méthode)
POST /user/mfa/totp/init Initialisation TOTP (QR code, secret)
POST /user/mfa/totp/verify Vérification du code TOTP initial
POST /user/mfa/disable Désactivation MFA
POST /user/mfa/recovery/regenerate Régénération des codes de récupération
POST /auth/reauth Re-authentification pour opérations sensibles

PD-106 est bloquée sur sa partie MFA tant que PD-238 n'est pas implémentée. La partie profil de PD-106 peut avancer car elle consomme PD-32 (déjà implémenté).

Relation avec PD-27

PD-27 définit la validation MFA (vérifier qu'un utilisateur est authentifié MFA). PD-238 définit la gestion MFA (permettre à l'utilisateur de configurer son MFA). Ces deux stories sont complémentaires : - PD-27 : "L'utilisateur est-il authentifié MFA ?" (middleware de validation) - PD-238 : "L'utilisateur peut-il activer/désactiver son MFA ?" (endpoints de configuration)

Intégration Keycloak

Les endpoints PD-238 délèguent la gestion MFA à Keycloak via son Admin REST API. Le backend ProbatioVault agit comme proxy avec un service account admin pour : - Récupérer l'état MFA d'un utilisateur - Initialiser la configuration TOTP - Valider le code TOTP initial - Désactiver le MFA - Gérer les codes de récupération (15 codes par défaut Keycloak)


2. Objectifs principaux

  • Permettre à un utilisateur authentifié de consulter son état MFA (actif/inactif, méthode configurée).
  • Permettre à un utilisateur d'activer le MFA TOTP via un flux sécurisé (secret + QR code + vérification).
  • Permettre à un utilisateur de désactiver son MFA avec re-authentification obligatoire.
  • Permettre à un utilisateur de régénérer ses codes de récupération avec invalidation des anciens.
  • Fournir les endpoints attendus par le client mobile (PD-106) pour l'écran Settings/MFA.
  • Appliquer un rate limiting sur ces endpoints pour prévenir les abus.

3. Non-objectifs (exclusions explicites)

  • Pas de validation MFA à l'authentification — couvert par PD-27.
  • Pas de gestion de profil — couvert par PD-32.
  • Pas de changement de mot de passe — user story distincte à créer.
  • Pas de suppression de compte — user story distincte à créer.
  • Pas de logout — user story distincte à créer.
  • Pas de MFA par SMS, email ou WebAuthn — seul TOTP est supporté dans cette version.
  • Pas d'implémentation TOTP native — délégation à Keycloak.

4. Contraintes

Sécurité

  • Authentification obligatoire : tous les endpoints /user/mfa/* requièrent un JWT valide.
  • Re-authentification pour opérations sensibles : désactivation MFA et régénération des codes de récupération exigent une re-authentification récente (< 5 minutes).
  • Secrets jamais loggués : le secret TOTP et les codes de récupération ne doivent jamais apparaître dans les logs applicatifs.
  • Rate limiting : protection contre les tentatives de bruteforce sur la vérification TOTP.
  • RLS : un utilisateur ne peut gérer que son propre MFA.

Techniques

  • Cohérence avec PD-106 : le contrat d'API doit être compatible avec ce que le client mobile attend.
  • Intégration Keycloak : utilisation de l'Admin REST API ou Account API de Keycloak pour la gestion MFA.
  • Cohérence avec l'architecture existante : NestJS, DTOs avec class-validator, Swagger, guards existants.

Réglementaires

  • Pas de stockage de secrets MFA côté ProbatioVault : tous les secrets sont gérés par Keycloak (conformité PD-27 §I3).

5. Scénarios d'échec et résultats inacceptables

  • Fuite du secret TOTP : le secret apparaît dans les logs, réponses d'erreur ou est persisté côté backend.
  • Activation MFA sans vérification : le MFA est activé sans que l'utilisateur ait prouvé qu'il possède le code TOTP.
  • Désactivation MFA sans re-authentification : un attaquant avec une session volée peut désactiver le MFA.
  • Codes de récupération non invalidés : après régénération, les anciens codes restent valides.
  • Accès croisé : un utilisateur A peut consulter ou modifier le MFA d'un utilisateur B.
  • Incohérence avec PD-106 : le client mobile reçoit un contrat d'API différent de ce qu'il attend.

6. Endpoints cibles

6.1 GET /user/mfa/status

Retourne l'état MFA de l'utilisateur courant.

Réponse attendue :

{
  "enabled": true,
  "method": "totp",
  "configuredAt": "2026-01-15T10:30:00Z"
}

6.2 POST /user/mfa/totp/init

Initialise la configuration TOTP. Retourne le secret et l'URI pour générer un QR code.

Réponse attendue :

{
  "secret": "JBSWY3DPEHPK3PXP",
  "qrCodeUri": "otpauth://totp/ProbatioVault:user@example.com?secret=JBSWY3DPEHPK3PXP&issuer=ProbatioVault",
  "expiresAt": "2026-02-06T12:00:00Z"
}

Note : le secret n'est pas encore actif, il doit être vérifié via /totp/verify.

6.3 POST /user/mfa/totp/verify

Vérifie le code TOTP fourni par l'utilisateur et active le MFA si correct.

Body :

{
  "code": "123456"
}

Réponse succès :

{
  "enabled": true,
  "recoveryCodes": ["ABC123", "DEF456", "GHI789", ...]
}

Les codes de récupération ne sont retournés qu'une seule fois, à l'activation.

6.4 POST /user/mfa/disable

Désactive le MFA de l'utilisateur. Requiert re-authentification.

Headers requis : X-Reauth-Token ou mécanisme équivalent prouvant re-authentification récente.

Réponse succès :

{
  "enabled": false
}

6.5 POST /user/mfa/recovery/regenerate

Régénère les codes de récupération. Invalide tous les codes précédents. Requiert re-authentification.

Réponse succès :

{
  "recoveryCodes": ["NEW123", "NEW456", "NEW789", ...]
}

6.6 POST /auth/reauth

Endpoint de re-authentification pour les opérations sensibles.

Body :

{
  "password": "current-password"
}

Réponse succès :

{
  "reauthToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "expiresAt": "2026-02-06T12:05:00Z"
}

Le token est un JWT signé avec : - sub : ID utilisateur - purpose : "reauth" (évite réutilisation pour autre chose) - exp : expiration à 5 minutes

Le client envoie ce token via header X-Reauth-Token pour les opérations sensibles.


7. Décisions techniques

Question Décision Justification
API Keycloak Admin API Le backend agit comme proxy avec service account. Permet validations métier avant action. Cohérent avec l'architecture (backend = point d'entrée unique).
Codes de récupération 15 codes Défaut Keycloak, évite configuration custom.
Format reauthToken JWT signé Stateless, cohérent avec auth existante. Claims : sub, purpose: "reauth", exp (TTL 5 min).