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'
OidcJwtAuthGuardexistant (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
DiscoveryServicepermettait l'inventaire automatique des endpoints
3. Points difficiles¶
3.1 Création du guard composite¶
- Le
SessionValidationGuarddevait à la fois réutiliserOidcJwtAuthGuardet 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
JwtValidationError→JustificationCoden'é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.getMetadatautilisé 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
vitestmais le projet utilise Jest - Tous les imports
vi.fn()ont dû être convertis enjest.fn() - La propriété
namedes classes est read-only en JavaScript, nécessitantObject.defineProperty
4. Hypothèses révélées tardivement¶
4.1 Claim jti optionnel dans le DTO¶
- Découverte : Le
OidcJwtPayloadexistant ne contenait pas le champjti - Impact : Nécessité de modifier le DTO partagé
- Résolution : Ajout du champ
jti?: stringavec documentation PD-28
4.2 Absence de type Mock global dans Jest¶
- Découverte : Les fichiers de test référençaient
Mockde 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 :
SessionRevocationListeneravec 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