Aller au contenu

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

1. Résumé exécutif

Objectif initial : Intégration côté backend NestJS d'un fournisseur d'identité OAuth2/OIDC (Keycloak) avec validation JWT complète, modèle d'autorisation HYBRID (roles + scopes), isolation multi-tenant, journalisation conforme et fail-closed en cas d'indisponibilité JWKS.

Résultat obtenu : Module OidcAuthModule complet avec : - Service de découverte OIDC et gestion JWKS (OidcDiscoveryService) - Validation JWT conforme à la spécification normative (OidcJwtValidationService) - Guards d'authentification et d'autorisation (OidcJwtAuthGuard, AuthorizationGuard) - Intercepteur d'audit avec liste blanche fermée (AuthAuditInterceptor) - Health indicator pour signal fail-closed (AuthHealthIndicator) - Décorateurs d'autorisation (@Roles, @Scopes, @AuthorizationRule) - Configuration normative (oidc.config.ts, auth.config.ts, audit.config.ts)

Verdict d'acceptabilité : ✅ ACCEPTÉ (2026-01-08)

État des tests contractuels : - Vitest OIDC (jose ESM) : 38/38 PASS - Jest auth suites : 25 suites / 389 tests PASS - Tests PD-235 délégués : couverts via alias TC-PD235-*


2. Points fluides

Spécification normative complète

  • Invariants explicites (INV-01 à INV-11) avec justifications de sécurité
  • Annexe §16 avec valeurs contractuelles concrètes (issuer, audiences, algorithmes, claims)
  • Politique de log/audit avec liste blanche fermée (§14)
  • Modèle d'autorisation HYBRID clairement défini

Architecture NestJS idiomatique

  • Guards globaux via APP_GUARD (ordre : auth puis authorization)
  • Intercepteur global via APP_INTERCEPTOR pour audit
  • Configuration via @nestjs/config avec registerAs()
  • Health indicator intégré à @nestjs/terminus
  • Décorateurs custom pour l'autorisation déclarative

Séparation des responsabilités claire

  • OidcDiscoveryService : découverte + gestion JWKS + fail-closed
  • OidcJwtValidationService : validation pure du token
  • OidcJwtAuthGuard : extraction token + protection routes
  • AuthorizationGuard : évaluation roles/scopes HYBRID

Tests contractuels traçables

  • Identifiants TC-* dans les noms de tests
  • Couverture explicite de chaque invariant et critère d'acceptation
  • Alias TC-PD235-* pour la délégation PD-235

3. Points difficiles

Compatibilité jose ESM avec Jest

La bibliothèque jose v6+ est ESM-only, incompatible avec le loader Jest CommonJS par défaut. - Détection : Erreurs de syntaxe à l'import des modules jose dans Jest - Résolution : Création d'une configuration Vitest dédiée (vitest.config.oidc.ts) pour les tests utilisant jose, maintien de Jest pour le reste - Impact : Deux runners de tests à exécuter (Vitest + Jest)

Écarts initiaux d'acceptabilité (E-01 à E-06)

La revue initiale a identifié 6 écarts majeurs entre le plan et la spécification :

Écart Description Résolution
E-01 Absence de preuves d'exécution des tests Exécution documentée Vitest + Jest
E-02 Signal fail-closed JWKS non documenté AuthHealthIndicator avec HealthCheckError
E-03 Contrôles realm/tenant non détaillés validateTenant() avec liste fermée
E-04 Liste blanche logs non reprise AUDIT_ALLOWED_FIELDS + FORBIDDEN_PATTERNS
E-05 Modèle HYBRID non explicite validateAuthzClaim() + AuthorizationGuard
E-06 Tolérance temporelle non documentée clockSkewMax: 120 dans config et validation

Délégation PD-235

Les tests d'erreur côté consumer (indisponibilité IdP, token sans identité) devaient être pris en charge par PD-26 : - Détection : Revue croisée PD-235 / PD-26 - Résolution : Section 2bis dans PD-26-tests.md avec alias TC-PD235-* - Traçabilité : Mapping explicite dans le plan d'implémentation (§5bis)


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

ID Hypothèse Moment de découverte Impact
- jose v6+ est ESM-only Configuration des tests Double runner Vitest + Jest
- Tests PD-235 délégués au backend Revue d'acceptabilité Ajout section 2bis et §5bis dans le plan
H-T05 Horloge synchronisée < 120s Validation temporelle Tolérance explicite à documenter

5. Invariants complexes

INV-08 — Fail-closed JWKS

  • Complexité : Signal observable distinct de HTTP 401 (token invalide)
  • Risque régression : Confusion 401 vs 503
  • Implémentation : jwksAvailable flag + ServiceUnavailableException + AuthHealthIndicator
  • Couverture : TC-NOM-11, TC-PD235-ERR-01, TC-PD235-ERR-03

INV-09/10/11 — Modèle HYBRID déterministe

  • Complexité : Combinaison AND/OR explicite, refus si ni roles ni scopes
  • Risque régression : Règle implicite, héritage non déclaré
  • Implémentation : @AuthorizationRule(AND|OR) obligatoire si roles + scopes déclarés
  • Couverture : TC-NOM-08, TC-NOM-12, TC-NR-02

INV-04 — Non-fuite dans les logs

  • Complexité : Liste blanche fermée + détection patterns interdits
  • Risque régression : Nouveau champ ajouté sans revue
  • Implémentation : AUDIT_ALLOWED_FIELDS const + AUDIT_FORBIDDEN_PATTERNS regex
  • Couverture : TC-NOM-10, TC-NEG-04

6. Dette technique

Dette Raison d'acceptation Impact Remédiation prévue
Double runner tests (Vitest + Jest) Compatibilité jose ESM Complexité CI Migration Jest vers Vitest complète
Pas de métriques Prometheus exposées MVP Observabilité limitée Ajout @willsoto/nestjs-prometheus

Note : Les tests E2E avec Keycloak réel sont couverts par PD-235 (infra) via les scripts tests/pd-235/*.sh exécutés en CI contre l'environnement déployé.


7. Risques résiduels

Risque Probabilité Impact Mitigation actuelle Surveillance
Dérive horloge > 120s Faible Majeur (tokens rejetés) NTP configuré Monitoring NTP
Rotation clés JWKS mal gérée Moyenne Majeur Refresh périodique + retry Logs refresh
Injection dans logs d'audit Faible Critique FORBIDDEN_PATTERNS Tests TC-NEG-04
Confusion 401 vs 503 Faible Mineur Error codes distincts Monitoring HTTP codes

8. Améliorations de processus

Documentation écarts dans le plan d'implémentation

La revue initiale a identifié 6 écarts majeurs entre le plan et la spécification. Recommandation : - Inclure une checklist de conformité spec→plan avant validation du plan - Vérifier explicitement chaque invariant (INV-) et critère (CA-) dans le plan

Gestion des dépendances ESM

La bibliothèque jose v6+ étant ESM-only, les projets NestJS utilisant Jest doivent : - Anticiper la compatibilité ESM lors du choix des bibliothèques - Prévoir Vitest pour les modules ESM-only - Documenter la configuration des tests multi-runners

Traçabilité des délégations entre User Stories

La délégation des tests PD-235 vers PD-26 a nécessité une coordination explicite : - Créer une section dédiée dans les tests (2bis) avec alias - Ajouter un mapping dans le plan d'implémentation (§5bis) - Référencer la dépendance dans les documents des deux US


9. Enseignements clés

  1. Compatibilité ESM/CommonJS : Les bibliothèques ESM-only (jose v6+) nécessitent Vitest ou une configuration Jest complexe. Anticiper ce choix dès la sélection des dépendances.

  2. Écarts plan/spécification : Une revue systématique du plan contre les invariants et critères d'acceptation évite les écarts découverts tardivement en acceptabilité.

  3. Signal fail-closed observable : Distinguer clairement HTTP 503 (service dégradé) de HTTP 401 (token invalide) pour une observabilité correcte et des alertes pertinentes.

  4. Liste blanche de logs fermée : Définir les champs autorisés en amont avec des constantes typées évite les fuites accidentelles de données sensibles.

  5. Modèle d'autorisation explicite : Le décorateur @AuthorizationRule(AND|OR) rend la combinaison roles+scopes déterministe et testable, évitant les ambiguïtés d'héritage.

  6. Délégation inter-US : Les tests délégués d'une US infra (PD-235) vers une US backend (PD-26) doivent être explicitement tracés avec des alias pour assurer la clôture.


Références