Aller au contenu

PD-238 — Livrable Agent Developer : MFA Management Service & Controller

Tâche 4 du manifest de décomposition Date : 2026-02-06 Agent : agent-developer (Claude)

Périmètre

  • src/modules/auth/mfa/services/mfa-management.service.ts
  • src/modules/auth/mfa/controllers/mfa-management.controller.ts
  • src/modules/auth/guards/mfa-rate-limit.guard.ts
  • src/modules/auth/mfa/mfa.module.ts
  • src/modules/auth/auth.module.ts

Fichiers créés/modifiés

1. mfa-rate-limit.guard.ts (nouveau)

Guard CanActivate avec rate limiting Redis per-userId : - Clé: rate_limit:mfa:{userId}, max 10 req/min, TTL 60s - Identifiant: userId extrait de request.user.sub (JWT) - Throw MfaRateLimitError (ERR-238-RATE-LIMIT, HTTP 429) si dépassé - Pattern Redis identique à RateLimitService (INCR + EXPIRE atomiques) - setRedisClient() pour injection en tests

2. mfa-management.service.ts (nouveau)

5 méthodes publiques : - getStatus(userId) : récupère état MFA via KeycloakAdminService - initTotp(userId) : stocke sessionId en Redis, ne le retourne PAS au client - verifyTotp(userId, code) : récupère sessionId du cache, le supprime après activation réussie - disable(userId) : récupère credentialId via getMfaStatus, puis disableMfa - regenerateRecoveryCodes(userId) : récupère credentialId, appelle Keycloak

Fonctionnalités : - Stockage sessionId TOTP en Redis (mfa:totp:session:{userId}, TTL 5min) - Émission d'événements sécurité via SecurityEventEmitter - INV-238-09 : aucun secret loggé (messages génériques)

3. mfa-management.controller.ts (nouveau)

5 endpoints sous @Controller('user/mfa') :

Endpoint Guards
GET /user/mfa/status OidcJwtAuthGuard, MfaRateLimitGuard
POST /user/mfa/totp/init OidcJwtAuthGuard, MfaRateLimitGuard
POST /user/mfa/totp/verify OidcJwtAuthGuard, MfaRateLimitGuard
POST /user/mfa/disable OidcJwtAuthGuard, ReauthTokenGuard, MfaRateLimitGuard
POST /user/mfa/recovery/regenerate OidcJwtAuthGuard, ReauthTokenGuard, MfaRateLimitGuard

Décorateurs classe : - @UseFilters(MfaExceptionFilter) - @UseGuards(OidcJwtAuthGuard) (INV-238-01) - @ApiTags('mfa')

4. mfa.module.ts (modifié)

  • Imports ajoutés : JwtModule.registerAsync(...), ConfigModule
  • Providers ajoutés : KeycloakAdminService, MfaManagementService, ReauthTokenGuard, MfaRateLimitGuard, MfaExceptionFilter
  • Controllers ajoutés : MfaManagementController
  • Exports ajoutés : MfaManagementService, ReauthTokenGuard

5. auth.module.ts (modifié)

  • Imports ajoutés : MfaModule (pour SecurityEventEmitter)
  • Providers ajoutés : ReauthService
  • Controllers ajoutés : ReauthController

Conformité invariants

Invariant Statut
INV-238-01 Conforme — @UseGuards(OidcJwtAuthGuard) au niveau classe
INV-238-02 Conforme — userId via @CurrentUser()user.sub
INV-238-03 Conforme — getStatus retourne
INV-238-04 Conforme — initTotp retourne
INV-238-05 Conforme — Activation uniquement si code valide
INV-238-06 Conforme — disable protégé par ReauthTokenGuard
INV-238-07 Conforme — regenerateRecoveryCodes protégé par ReauthTokenGuard
INV-238-08 Conforme — Recovery codes retournés uniquement après activation/régénération
INV-238-09 Conforme — Aucun secret loggé
INV-238-11 Conforme — MfaRateLimitGuard appliqué (10 req/min/userId)

Hypothèses documentées

  1. Redis partagé : Le guard MFA rate limit et le service MFA session utilisent chacun leur propre connexion Redis avec la même configuration.

  2. JwtModule dupliqué : MfaModule importe JwtModule.registerAsync(...) pour que ReauthTokenGuard dispose d'un JwtService.

  3. MfaModule importé dans AuthModule : Nécessaire pour que ReauthService ait accès à SecurityEventEmitter. Pas de dépendance circulaire.

Fichiers hors périmètre identifiés

Aucun.