Aller au contenu

Analyse 2FA — ProjetESM : état actuel et cible

Date : 2026-06-20
Projet : /home/kanmaber/projets/ProjetESM
Référence : 20260620_1200_analyse-2fa-gestionFichePaie.md · 20260620_1201_analyse-2fa-PsychoEnLigne-vs-gestionFichePaie.md


État actuel de ProjetESM

ProjetESM est une synthèse intermédiaire : il a hérité des fonctionnalités avancées de PsychoEnLigne (blocage IP, détection suspectes, journal sécurité, révocation sessions), mais porte encore les bugs de gestionFichePaie.

Fonctionnalité État
Secret TOTP chiffré deuxFacteursSecret String? — plain text
Backup code → connexion réelle ❌ retourne {codeValide} sans JWT
Renvoi code EMAIL (API réelle) twoFactorApi.sendCode() appelé
Debug console.log supprimé Activer2FAUseCase.ts:129,185
Backup codes sans orphelins ❌ 2 lots créés (Generer + Activer)
Blocage IP / détection suspectes ✅ déjà présent
Journal de sécurité dans l'UI ✅ déjà présent
Gestion sessions dans l'UI ✅ déjà présent
Tests unitaires auth ❌ zéro

Backend — cible (meilleur des deux projets)

Fonctionnalités à conserver (déjà présentes — PsychoEnLigne)

  • BloquerIPUseCase + DebloquerIPUseCase
  • DetecterConnexionsSuspectesUseCase
  • ObtenirLogsSecuriteUseCase
  • RevoquerSessionUseCase, RevoquerToutesSessionsUseCase, RevoquerTousTokensUseCase
  • ListerSessionsActivesUseCase
  • VerifierEmailUseCase (email obligatoire à l'inscription)

Corrections à apporter

1. Chiffrer le secret TOTP au repos (PsychoEnLigne)

Schema Prisma :

- deuxFacteursSecret  String?  @db.VarChar(255)
+ secret2FAChiffre    String?  // AES-256 — jamais en plain text

Service2FA : ajouter chiffrerSecret(secret: string) et dechiffrerSecret(chiffre: string) via crypto.createCipheriv('aes-256-gcm', ...) avec clé dans .env (TOTP_ENCRYPTION_KEY).

2. Supprimer les console.log de debug (PsychoEnLigne)

Activer2FAUseCase.ts:129 et :185 — remplacer par this.logger.debug(...) ou supprimer.

3. Corriger VerifierBackupCodeUseCase pour qu'il complète la connexion

Aucun des deux projets ne l'a fait correctement. Ce use case doit retourner des JWT exactement comme Verifier2FAUseCase :

// Après bcrypt.compare(code, codeHache) → OK
const { accessToken, refreshToken } = await this.serviceJWT.genererPaireTokens(tokenPayload);
const session = await this.sessionRepository.creerSession({ ... });
return {
  succes: true,
  codeValide: true,
  accessToken: accessToken.token,
  refreshToken: refreshToken.token,
  expiresIn: ...,
  utilisateur: { ... },
};

4. Corriger les backup codes orphelins

Generer2FASecretUseCase — retourner les codes en clair sans les stocker :

const backupCodes = this.service2FA.genererBackupCodes(10);
// NE PAS appeler backupCodeRepository.creer() ici
return { secret2FA: { ..., backupCodes } };

Activer2FAUseCase — seul point de persistance des codes hashés, après confirmation.

Ajout à planifier

  • Tests unitaires sur Verifier2FAUseCase, Activer2FAUseCase, VerifierBackupCodeUseCase (modèle PsychoEnLigne — un fichier .test.ts par use case)
  • Supprimer Type2FA.SMS du shared-kernel ou implémenter un ServiceSMS réel

Frontend — cible (meilleur des deux projets)

Points forts de PsychoEnLigne à conserver / aligner

  • Architecture DDD complète (domain/entities, value-objects, ports) — déjà en place
  • TwoFactorSetupPage sans bouton "Configurer plus tard" — déjà OK (pas de page setup problématique)
  • SessionsManager + SecuritySettings dans l'UI — déjà présents
  • Code propre sans logs parasites

Point fort de gestionFichePaie à conserver

  • handleResendCode appelle réellement twoFactorApi.sendCode()déjà présent dans ProjetESM

Correction à apporter

authStore.verifyBackupCode doit connecter l'utilisateur :

// Actuellement — bug : retourne boolean, ne connecte pas
verifyBackupCode: async (data) => {
  const isValid = await container.verifyBackupCodeUseCase.execute(data);
  return isValid; // ← l'utilisateur reste déconnecté
}

// Cible — après fix du backend qui retourne des JWT
verifyBackupCode: async (data) => {
  set({ isLoading: true, error: null });
  try {
    const result = await container.verifyBackupCodeUseCase.execute(data);
    tokenManager.setTokens(result.accessToken, result.refreshToken);
    set({
      isLoading: false,
      isAuthenticated: true,
      requires2FA: false,
      twoFactorSessionData: null,
      user: result.utilisateur,
      error: null,
    });
    startBackgroundRefresh(result.refreshToken);
  } catch (error) {
    set({ isLoading: false, error: error.message });
    throw error;
  }
}

Ce qui manque dans les deux projets (à ajouter)

  • "Se souvenir de cet appareil" (optionnel) : token de confiance 30 jours en cookie httpOnly, coté backend AppareillDeConfianceRepository + vérification dans ConnexionUseCase avant de déclencher le flow 2FA.

Tableau récapitulatif — les trois projets vs cible

Fonctionnalité gestionFichePaie PsychoEnLigne ProjetESM actuel ProjetESM cible
Secret TOTP chiffré ✅ AES-256
Backup code → connexion réelle ✅ à implémenter
Renvoi code EMAIL (API) ✅ déjà OK
Debug console.log supprimé ✅ à nettoyer
Backup codes sans orphelins ✅ à corriger
Blocage IP / détection suspectes ✅ déjà OK
Journal de sécurité dans l'UI ✅ déjà OK
Gestion sessions dans l'UI ✅ déjà OK
Pas de bouton "skip" 2FA setup ✅ déjà OK
Tests unitaires auth partiels ✅ complets ❌ zéro ✅ à écrire
"Se souvenir de cet appareil" planifiable

Priorités d'implémentation

# Action Effort Impact
1 Fix VerifierBackupCodeUseCase → retourner JWT Moyen 🔴 Bloquant
2 Fix authStore.verifyBackupCodeisAuthenticated: true Faible 🔴 Bloquant
3 Chiffrement AES-256 secret TOTP (schema + service) Moyen 🟠 Sécurité
4 Supprimer console.log debug Faible 🟠 Sécurité
5 Fix backup codes orphelins (Generer ne stocke pas) Faible 🟠 Intégrité données
6 Tests unitaires use cases 2FA Élevé 🟡 Qualité
7 Supprimer Type2FA.SMS ou implémenter Faible 🟡 Cohérence