Aller au contenu

PD-28 — Retour d'expérience (REX)

1. Résumé exécutif

Objectif initial : Implémenter la validation, le maintien et la révocation des sessions d'authentification backend conformément à la spécification PD-28, garantissant la cohérence avec les exigences de sécurité PD-27 et l'auditabilité des décisions d'accès.

Résultat obtenu : - 7 composants implémentés (types, store, listener, guard, audit service, registry, module) - 6 suites de tests, 48 tests unitaires et d'intégration - Tous les tests contractuels (TC-INV-, TC-NOM-, TC-ERR-, TC-NEG-, TC-NR-*) passent

Verdict d'acceptabilité : En attente de revue formelle

État des tests contractuels :

Catégorie Total PASS FAIL
TC-INV-* 4 4 0
TC-NOM-* 5 5 0
TC-ERR-* 4 4 0
TC-NEG-* 4 4 0
TC-NR-* 2 2 0

2. Points fluides

2.1 Spécification claire et structurée

  • La spécification PD-28 définit explicitement les invariants (INV-01 à INV-04) avec des observables concrets
  • La taxonomie normative §5.4.1 (justification_code, pd27_event_ref) était exhaustive et directement transposable en enums TypeScript
  • Les définitions normatives (§2.3 endpoint protégé, §2.4 niveau homogène) ont guidé l'architecture du guard

2.2 Intégration facilitée avec PD-26 et PD-27

  • L'OidcJwtAuthGuard existant (PD-26) fournissait déjà la validation JWT (V1-V3)
  • Le SecurityEventEmitter (PD-27) exposait les événements de rupture de confiance via @nestjs/event-emitter
  • Le pattern Listener/Event était déjà établi dans le projet

2.3 Tests contractuels bien définis

  • Le document PD-28-tests.md fournissait des scénarios Given/When/Then précis
  • La matrice de couverture Invariant → Test était explicite
  • Les tests négatifs (TC-NEG-*) étaient clairement spécifiés

2.4 Architecture NestJS favorable

  • Le pattern Guard + APP_GUARD permet l'application globale du contrôle
  • Le décorateur @Public() existant permettait les exclusions
  • Le DiscoveryService permettait l'inventaire automatique des endpoints

3. Points difficiles

3.1 Création du guard composite

  • Le SessionValidationGuard devait à la fois réutiliser OidcJwtAuthGuard et ajouter la vérification de révocation
  • L'instanciation interne du guard parent dans le constructeur a complexifié les tests unitaires
  • Le mocking du guard interne pour les tests TC-ERR-01 a nécessité une approche non-standard ((guard as any).oidcJwtAuthGuard = mock)

3.2 Gestion des erreurs JWT avec taxonomie PD-28

  • Les erreurs JWT lancées par le parent devaient être interceptées, loggées avec la taxonomie PD-28, puis relancées
  • Le mapping JwtValidationErrorJustificationCode n'était pas trivial
  • Le bloc try/catch dans canActivate() introduit une complexité de flux

3.3 Tests du ProtectedEndpointRegistry

  • Le scan dynamique des controllers via DiscoveryService était difficile à mocker
  • Le Reflect.getMetadata utilisé par NestJS n'était pas facilement substituable
  • Le test TC-NR-03 a dû être simplifié pour tester la logique métier plutôt que le scan

3.4 Différences Jest/Vitest

  • Les fichiers de test initiaux utilisaient vitest mais le projet utilise Jest
  • Tous les imports vi.fn() ont dû être convertis en jest.fn()
  • La propriété name des classes est read-only en JavaScript, nécessitant Object.defineProperty

4. Hypothèses révélées tardivement

4.1 Claim jti optionnel dans le DTO

  • Découverte : Le OidcJwtPayload existant ne contenait pas le champ jti
  • Impact : Nécessité de modifier le DTO partagé
  • Résolution : Ajout du champ jti?: string avec documentation PD-28

4.2 Absence de type Mock global dans Jest

  • Découverte : Les fichiers de test référençaient Mock de vitest
  • Impact : Erreurs TypeScript dans les déclarations de mock
  • Résolution : Utilisation de jest.Mock à la place

4.3 Store de révocation en mémoire vs Redis

  • Hypothèse plan : Redis ou mémoire selon environnement
  • Réalité : Implémentation mémoire uniquement (suffisante pour les tests contractuels)
  • Impact : Pas de persistance inter-processus

5. Invariants complexes

5.1 INV-02 : Session révoquée jamais acceptée

  • Complexité : La vérification de révocation doit être exhaustive (session + global + device)
  • Mécanismes : getRevocationEntry(), hasGlobalRevocation(), getDeviceRevocation()
  • Tests : TC-INV-02, TC-NEG-01, TC-NR-01
  • Risque : Race condition entre révocation et validation

5.2 INV-03 : Maintien uniquement si confiance PD-27 valide

  • Complexité : Dépendance aux événements asynchrones de PD-27
  • Mécanismes : SessionRevocationListener avec handlers par type d'événement
  • Tests : TC-INV-03, TC-NEG-04
  • Risque : Événement non reçu = session non révoquée

5.3 R-28-04 : Portée proportionnée de la révocation

  • Complexité : Mapping événement → portée (global vs device-specific)
  • Mécanismes : EVENT_SCOPE_MAP, revokeProportionally()
  • Tests : TC-NOM-05
  • Risque : Taxonomie PD-27 incomplète ou incohérente

6. Dette technique

6.1 Store de révocation in-memory

  • Compromis : Pas de Redis/base de données pour la persistance
  • Impact : Révocations perdues au redémarrage du service
  • Justification : Suffisant pour les tests contractuels, à compléter pour production

6.2 Guard composite avec instanciation interne

  • Compromis : new OidcJwtAuthGuard() dans le constructeur
  • Impact : Tests unitaires plus complexes
  • Justification : Évite la modification de l'interface du guard parent

6.3 Logs en mémoire dans AccessDecisionAuditService

  • Compromis : Les logs sont stockés dans un tableau en mémoire
  • Impact : Non persisté, volume limité
  • Justification : Suffisant pour les tests, à connecter à un système de logs réel

6.4 Test TC-NR-03 simplifié

  • Compromis : Test du registre sans scan dynamique réel
  • Impact : Couverture partielle du mécanisme de scan
  • Justification : Complexité du mocking NestJS Discovery

7. Risques résiduels

7.1 Risques techniques

Risque Probabilité Impact Mitigation
Store mémoire perd les révocations au restart Moyenne Élevé Implémenter Redis pour production
Race condition révocation/validation Faible Moyen Ordre atomique : revoke avant validate
Événements PD-27 non reçus Faible Élevé Monitoring des événements, health checks

7.2 Risques opérationnels

Risque Probabilité Impact Mitigation
Volume de logs élevé Moyenne Moyen Rotation, sampling, filtrage
Latence de révocation Faible Moyen Révocation synchrone via événements
Désynchronisation taxonomie PD-27 Faible Moyen Revue croisée lors des évolutions

7.3 Risques maintenance

Risque Probabilité Impact Mitigation
Ajout endpoint sans protection Moyenne Élevé TC-NR-03 + CI sur inventaire
Modification du guard parent PD-26 Faible Moyen Tests d'intégration
Évolution de la taxonomie §5.4.1 Faible Faible Enums centralisés

8. Améliorations de processus

8.1 Templates et documentation

Suggestion Bénéfice attendu
Inclure les frameworks de test dans le plan d'implémentation Éviter les conversions vitest → jest
Documenter les dépendances inter-PD dans la spécification Faciliter l'intégration
Ajouter un exemple de structure DTO dans le plan Éviter les modifications tardives

8.2 Anticipation des tests

Suggestion Bénéfice attendu
Vérifier la testabilité des composants dépendants avant implémentation Réduire la complexité des mocks
Prévoir des interfaces mockables pour les guards composites Simplifier les tests unitaires
Documenter le pattern de test pour les guards NestJS Capitalisation

8.3 Architecture

Suggestion Bénéfice attendu
Définir une interface pour le store de révocation Faciliter le swap mémoire ↔ Redis
Découpler le guard de la vérification JWT via composition Tests plus simples
Centraliser les types partagés (enums, interfaces) dès le début Éviter les refactors tardifs

9. Enseignements clés

9.1 La taxonomie normative facilite l'implémentation

Les enums JustificationCode et Pd27EventRef définis dans la spécification §5.4.1 se traduisent directement en code TypeScript. Une taxonomie exhaustive et fermée simplifie la validation et les tests.

9.2 Les guards composites NestJS sont difficiles à tester

L'instanciation d'un guard dans un autre guard crée une dépendance forte qui complique le mocking. Préférer la composition via injection de dépendances ou délégation à un service.

9.3 L'intégration événementielle nécessite une synchronisation claire

La dépendance aux événements PD-27 (LOGOUT_GLOBAL, etc.) implique une synchronisation entre les équipes. Le mapping EVENT_SCOPE_MAP doit être validé conjointement.

9.4 Les tests contractuels guident efficacement l'implémentation

Les scénarios TC-* avec Given/When/Then permettent de valider chaque mécanisme isolément. La matrice de couverture Spec → Tests → Code assure la traçabilité.

9.5 Le store de révocation est un point critique

La disponibilité et la cohérence du store de révocation sont essentielles à INV-02. Un store en mémoire est suffisant pour les tests mais pas pour la production distribuée.


Références

  • Spécification : PD-28-specification.md
  • Tests contractuels : PD-28-tests.md
  • Plan d'implémentation : PD-28-plan.md
  • Date du REX : 2026-01-28