Aller au contenu
Article

Headless Drupal : authentification sécurisée entre front et API

14 min

1. Ce que change l'architecture découplée sur le plan de l'authentification

Dans un Drupal monolithique classique, l'authentification repose sur un cookie de session géré par Symfony : le navigateur envoie le cookie, Drupal valide la session côté serveur, tout reste en première partie (same-origin). Les mécanismes de protection CSRF de Drupal fonctionnent de manière transparente.

Dans une architecture headless, ce modèle se fragmente : le front est servi depuis un domaine distinct, les appels API transitent en cross-origin, et les sessions navigateur perdent leur pertinence dès qu'une application mobile ou un contexte SSR est impliqué.

Chaque choix d'authentification en headless est un choix de surface d'attaque. Il n'y a pas de configuration qui convienne à tous les projets — il y a des patterns adaptés à des contextes précis.

2. Cookies ou tokens ? Le choix structurant

Cookie de session httpOnly

Drupal peut émettre un cookie de session httpOnly en mode headless, à condition que le front et l'API partagent le même domaine parent. Le token n'est jamais accessible en JavaScript, mais le pattern reste incompatible avec les applications mobiles natives et impose credentials: true en CORS.

JWT via Simple OAuth

Le JWT est le choix dominant en headless pur. Drupal expose un endpoint /oauth/token via le module Simple OAuth. Le token ne peut pas être révoqué avant son expiration — une courte durée de vie (5–15 min) est impérative, associée à un refresh token stocké en cookie httpOnly et soumis à rotation.

Pattern BFF (Backend For Frontend) pour Next.js

Pour les fronts Next.js avec SSR, le BFF agit comme proxy : il récupère le token OAuth côté serveur sans exposer le client_secret au navigateur, stocke le refresh token en cookie httpOnly, et injecte l'access token dans les appels Drupal. Le navigateur ne voit jamais ni le secret, ni le refresh token.

3. CSRF en contexte headless

La protection CSRF de Drupal repose sur un token envoyé dans le header X-CSRF-Token. Toute route qui modifie un état (POST, PATCH, DELETE) doit l'exiger, obtenu préalablement via GET /session/token.

Les JWT transmis par header Authorization: Bearer réduisent le risque CSRF (un attaquant ne peut pas injecter ce header cross-origin). Mais dès qu'un cookie entre dans l'équation, le CSRF doit être adressé explicitement.

4. Configuration CORS : précision plutôt que permissivité

En production, allowedOrigins: ['*'] est une erreur structurelle. La configuration minimale recommandée liste explicitement les origines autorisées, limite les headers à ceux utilisés, et fixe un maxAge raisonnable pour le cache des preflights. CORS protège le navigateur — pas l'API côté serveur. La vérification du token et les permissions Drupal sont les vrais gardiens.

5. Gestion des secrets et rotation

  • client_secret OAuth : vit uniquement côté serveur, jamais dans une variable NEXT_PUBLIC_ ni dans le dépôt Git. Rotation planifiée tous les 90 jours.
  • Clés de signature JWT : générées en RSA 2048 bits minimum, injectées via un gestionnaire de secrets (Vault, AWS Secrets Manager…), rotatives annuellement ou sur incident.
  • Refresh token : rotation obligatoire à chaque usage, stockage httpOnly uniquement en contexte navigateur, révocation sur déconnexion via /oauth/revoke.

6. Exposition minimale des scopes

Chaque client OAuth ne doit obtenir que les scopes strictement nécessaires. Un découpage pratique : content:read, content:write, user:profile, commerce:checkout, admin:config. Un token compromis avec un scope content:read ne permet pas d'écrire dans le CMS.

Auditez également les ressources JSON:API exposées et désactivez celles qui ne doivent pas être accessibles depuis l'extérieur.

7. Patrons d'architecture de référence

Next.js + Drupal avec BFF : le navigateur ne voit jamais le refresh token. Next.js gère l'échange OAuth côté serveur, stocke le refresh token en cookie httpOnly, et injecte l'access token dans les appels SSR vers Drupal.

Application mobile (PKCE) : le PKCE remplace le client_secret pour les apps publiques. Le code verifier est généré côté client, hashé en code challenge, et vérifié par Drupal lors de l'échange du code d'autorisation.

Machine-to-machine (client_credentials) : pas d'utilisateur dans la boucle. Le token a une courte durée de vie, pas de refresh token. Le client_secret vit dans le gestionnaire de secrets de l'infrastructure.

8. Check-list de tests de sécurité à automatiser

  • Token expiré → HTTP 401
  • JWT falsifié → rejet
  • Refresh token après rotation → ancien token invalide
  • Scope insuffisant → HTTP 403 sur route d'écriture
  • Révocation → token invalide immédiatement
  • Mutation sans X-CSRF-Token → HTTP 403
  • Requête depuis une origine non autorisée → pas de header CORS
  • Wildcard * absent en production avec credentials
  • Routes JSON:API sensibles → 403 sans scope approprié
  • Absence du client_secret dans le bundle JS front
  • Absence de clés dans l'historique Git
  • HTTPS obligatoire, HSTS activé

Conclusion

L'authentification dans un projet Drupal découplé n'est pas un problème de module — c'est un problème d'architecture. La décision cookie/token, la stratégie CSRF, la configuration CORS, la gestion des secrets et la granularité des scopes forment un système cohérent. Une seule décision mal prise compromet l'ensemble.

Si votre projet headless est en cours de conception ou si vous souhaitez auditer un système existant, demandez un diagnostic DrupaLabs.

Un projet web en tête ?

Commençons par un diagnostic : ce qui vous ralentit, et par où commencer.

Demander un diagnostic

Articles liés

Newsletter

Restez dans la boucle Drupal

Conseils, retours d'expérience et actualités Drupal — directement dans votre boîte mail, sans spam.

Pas de spam. Désinscription possible à tout moment.