Files
_Assistant_Lead_Tech/knowledge/backend/patterns/auth.md
MaksTinyWorkshop 7767f1f947 capitalisation: intégration 28 entrées knowledge + 2 CLAUDE.md RL799_V2 (triage branche mcp_v1)
28 nouvelles sections intégrées dans 12 fichiers knowledge (backend risques/patterns,
frontend risques/patterns, workflow risques). Couvre rate limiting, RGPD, CSP Next.js,
refresh token TOCTOU, catch-all Prisma, distinction 401/403, tests E2E Playwright, etc.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 20:11:02 +02:00

11 KiB

title: Backend — Patterns : Auth domain: backend bucket: patterns tags: [auth, requestid, api-errors, sessions, tokens] applies_to: [analysis, implementation, review, debug] severity: high validated_on: 2026-03-16 source_projects: [app-alexandrie]

Backend — Patterns : Auth

Extrait de la base de connaissance Lead_tech. Voir knowledge/backend/patterns/README.md pour l'index complet.


Pattern : Format d'erreur API standardisé

  • Objectif : fournir des erreurs prévisibles, exploitables et cohérentes pour tous les clients.
  • Contexte : API consommée par front-end, automatisations ou intégrations externes.
  • Quand l'utiliser : dès qu'une API est exposée à autre chose qu'un usage interne trivial.
  • Quand l'éviter : jamais.
  • Avantage :
    • Debug plus rapide
    • UX maîtrisée côté client
    • Observabilité améliorée
  • Limites / vigilance :
    • Discipline requise pour éviter les formats ad hoc
  • Validé le : 25-01-2026
  • Contexte technique : API HTTP agnostique

Implémentation (exemple minimal)

{
  "error": {
    "code": "USER_NOT_FOUND",
    "message": "Utilisateur introuvable",
    "requestId": "abc-123"
  }
}

Checklist

  • Codes HTTP cohérents (4xx / 5xx)
  • Codes d'erreur applicatifs stables
  • Message utilisateur non technique
  • requestId présent

Pattern : Middleware de corrélation (requestId / traceId)

  • Objectif : relier chaque requête aux logs et erreurs associées.
  • Contexte : toute API ou service exposé.
  • Quand l'utiliser : systématiquement en production.
  • Quand l'éviter : jamais.
  • Avantage :
    • MTTR réduit drastiquement
    • Debug cross-services possible
  • Limites / vigilance :
    • Doit être propagé partout (logs, erreurs, appels sortants)
  • Validé le : 25-01-2026
  • Contexte technique : Backend agnostique (HTTP)

Implémentation (exemple minimal)

- Générer un requestId à l'entrée si absent
- Le propager dans le contexte de requête
- L'inclure dans chaque log et réponse d'erreur

Checklist

  • requestId généré ou repris d'un header existant
  • Présent dans tous les logs
  • Présent dans les erreurs retournées

Pattern : Anti-énumération sur endpoints auth liés à un email

  • Objectif : empêcher qu'un endpoint auth révèle si un compte existe, n'existe pas ou n'est pas éligible.
  • Contexte : reset de mot de passe, invitation, vérification de compte, login ou tout flux qui part d'un email utilisateur.
  • Quand l'utiliser : dès qu'une requête auth touche un identifiant de type email.
  • Quand l'éviter : jamais sur une surface exposée.
  • Avantage :
    • réduit la fuite d'information sur les comptes existants
    • homogénéise les réponses côté client
    • se combine bien avec les garde-fous anti-abus
  • Limites / vigilance :
    • ne protège pas seul contre le brute-force, à combiner avec du rate-limiting
    • les logs internes doivent conserver la vraie cause sans l'exposer au client
  • Validé le : 16-03-2026
  • Contexte technique : Node.js / auth applicative / API HTTP

Implémentation (exemple minimal)

- retourner la même réponse HTTP 200 qu'un compte existe ou non
- ne jamais distinguer "email inconnu", "email connu" ou "compte OAuth-only" dans la réponse
- journaliser la cause réelle côté serveur
- ajouter un rate-limiting basé sur email + IP

Checklist

  • Réponse client uniforme pour les cas compte connu/inconnu/non éligible
  • Aucune fuite d'existence dans le message ou le code d'erreur
  • Rate-limiting présent sur les endpoints exposés
  • Logs internes exploitables

Pattern : Token à usage unique — génération, hash et invalidation atomique

  • Objectif : standardiser la création et la consommation de tokens sensibles sans stocker de secret brut en base.
  • Contexte : invitation, reset de mot de passe, vérification d'email, lien magique ou tout token one-shot.
  • Quand l'utiliser : pour tout token à usage unique transmis à l'utilisateur.
  • Quand l'éviter : sessions longues ou secrets devant être relus en clair côté serveur.
  • Avantage :
    • réduit l'impact d'une fuite de base
    • garde des tokens URL-safe
    • favorise une consommation atomique et réutilisable
  • Limites / vigilance :
    • la consommation doit rester atomique
    • la politique d'expiration doit être explicite
  • Validé le : 16-03-2026
  • Contexte technique : Node.js crypto / Prisma / email ou URL signée

Implémentation (exemple minimal)

- générer le token avec `crypto.randomBytes(32).toString("base64url")`
- stocker uniquement le hash SHA-256 du token en base
- transmettre le token brut uniquement via URL ou email
- recalculer le hash côté serveur lors de la consommation
- invalider le token dans une transaction atomique après usage

Checklist

  • Token brut jamais persisté en base
  • Hash recalculé côté serveur pour la vérification
  • Expiration explicite
  • Invalidation atomique après consommation

Pattern : Autorisation interne minimale sans RBAC complet

  • Objectif : sécuriser une capacité interne sensible sans ouvrir trop tôt un chantier RBAC complet.
  • Contexte : application avec peu de rôles, besoin ponctuel d'une capacité admin ou opérateur clairement identifiée.
  • Quand l'utiliser : quand une story métier demande un pouvoir interne limité mais réel.
  • Quand l'éviter : si les permissions deviennent nombreuses, hiérarchiques ou contextuelles.
  • Avantage :
    • sécurisation rapide et lisible d'une capacité sensible
    • source de vérité backend explicite
    • chemin d'évolution propre vers un RBAC plus complet
  • Limites / vigilance :
    • ne pas laisser proliférer des rôles ad hoc non gouvernés
    • ne remplace pas un vrai modèle de permissions si le domaine grossit
  • Validé le : 10-03-2026
  • Contexte technique : NestJS / auth par session ou JWT / API métier interne

Implémentation (exemple minimal)

- introduire un enum de rôle minimal côté backend (ex. USER | ADMIN)
- propager ce rôle dans la session ou le token d'auth
- créer un décorateur + guard dédiés pour la capacité sensible
- interdire les booléens front, emails hardcodés ou `if` dispersés dans les contrôleurs

Checklist

  • Le rôle vit dans la source de vérité backend
  • Le rôle est propagé dans le mécanisme d'auth existant
  • Les endpoints sensibles passent par un guard dédié
  • Aucun contrôle d'accès critique n'est piloté par le front
  • Le passage à RBAC reste possible sans casser le contrat existant

Pattern : Opérations auth sensibles — atomiques, idempotentes et cohérentes

  • Objectif : garantir que les opérations multi-étapes auth (reset, logout, révocation) ne laissent jamais un état incohérent.
  • Contexte : tout flux auth qui combine plusieurs writes : hash de mot de passe, invalidation de token, suppression de session.
  • Quand l'utiliser : systématiquement sur toute opération qui touche plusieurs tables auth en séquence.
  • Quand l'éviter : opérations de lecture pure.
  • Avantage :
    • pas de token valide après reset de mot de passe si l'opération est interrompue
    • suppression de session idempotente (P2025 absorbé silencieusement)
    • comportement prévisible même en cas de retry ou de concurrence
  • Limites / vigilance :
    • $transaction Prisma ne couvre pas les effets de bord réseau (email, cookies) — ces étapes restent hors transaction
  • Validé le : 16-03-2026
  • Contexte technique : Node.js / Prisma / auth par session ou token

Implémentation (exemple minimal)

// consumePasswordReset — atomique dans une transaction
await prisma.$transaction([
  prisma.passwordResetToken.update({
    where: { tokenHash },
    data: { consumedAt: new Date() },
  }),
  prisma.user.update({
    where: { id: userId },
    data: { passwordHash: newHash },
  }),
  prisma.session.deleteMany({ where: { userId } }),
]);

// Suppression de session — idempotente (P2025 absorbé)
try {
  await prisma.session.delete({ where: { sessionToken } });
} catch (err) {
  if (err?.code !== 'P2025') throw err; // session déjà supprimée → OK
}

Checklist

  • Toute opération hash + update + delete dans une $transaction
  • P2025 absorbé silencieusement sur les suppressions de session
  • Effets de bord hors transaction documentés (cookie, email)
  • Tests couvrant le cas d'une session déjà expirée

  • Objectif : limiter l'exposition du cookie refresh token au strict minimum.
  • Contexte : migration d'un stockage localStorage vers cookies httpOnly pour les tokens d'authentification.
  • Quand l'utiliser : dès qu'un refresh token est transmis via cookie.
  • Quand l'éviter : jamais.
  • Avantage :
    • réduit la surface d'attaque — le cookie ne voyage qu'avec les requêtes de refresh
    • évite l'envoi inutile du refresh token sur les endpoints auth (login, password, invitations)
  • Limites / vigilance :
    • vérifier que le path est compatible avec le routing réel de l'endpoint de refresh
  • Validé le : 08-04-2026
  • Contexte technique : auth / cookies httpOnly — RL799_V2

Règle

  • Le cookie refresh_token doit avoir Path=/api/auth/refresh (pas /api/auth). Seul l'endpoint de refresh a besoin de recevoir ce cookie.
  • Plus le path est large, plus la surface d'attaque est grande (le cookie voyage avec chaque requête matchant le path).

Checklist

  • Path=/api/auth/refresh sur le cookie refresh token
  • Path=/ uniquement pour l'access token (nécessaire sur toutes les routes API)
  • Vérifier que l'endpoint de refresh reçoit bien le cookie après changement de path

Pattern : Distinction stricte 401 vs 403

  • Objectif : permettre au frontend de réagir correctement aux erreurs d'authentification et d'autorisation.
  • Contexte : helper centralisé requireRoleAccess ou équivalent.
  • Quand l'utiliser : systématiquement sur tout helper d'authentification/autorisation.
  • Quand l'éviter : jamais.
  • Avantage :
    • le frontend peut distinguer "session expirée → redirection login" de "permission manquante → message accès refusé"
    • debug plus rapide en production
  • Limites / vigilance :
    • la distinction doit être cohérente sur tous les endpoints — ne pas retourner 403 pour un token absent sur certains services
  • Validé le : 08-04-2026
  • Contexte technique : auth / RBAC — RL799_V2

Règle

Le helper requireRoleAccess doit retourner :

  • 401 UNAUTHORIZED : token absent, expiré, malformé, ou email manquant dans le payload
  • 403 FORBIDDEN : token valide mais rôle non autorisé pour la ressource

Checklist

  • 401 pour tout problème de token (absent, expiré, malformé)
  • 403 uniquement quand le token est valide mais le rôle insuffisant
  • Frontend redirige vers /login sur 401, affiche "accès refusé" sur 403