JWT vs Session : quel mécanisme d'authentification choisir ?
Authentifier un utilisateur, c'est savoir à chaque requête qui il est. Deux grandes familles s'affrontent : les sessions serveur, où le serveur garde la trace de l'utilisateur connecté, et les JSON Web Tokens (JWT), où l'identité voyage dans un jeton signé porté par le client. Le choix influe sur la sécurité, la capacité à monter en charge et la facilité à déconnecter un utilisateur. Voici comment trancher selon votre contexte.
Les sessions serveur (stateful)
Avec une session, le serveur crée un identifiant de session à la connexion, stocke les données associées (identité, rôles, panier) côté serveur (mémoire, Redis, base de données) et renvoie au navigateur un cookie contenant seulement cet identifiant. À chaque requête, le serveur lit le cookie, retrouve la session et sait qui parle.
- État côté serveur : la source de vérité reste sur le serveur, le client ne porte qu'une référence opaque.
- Révocation immédiate : supprimer la session côté serveur déconnecte instantanément l'utilisateur.
- Cookie : transmis automatiquement par le navigateur, idéalement en
HttpOnly,SecureetSameSite.
Les JSON Web Tokens (stateless)
Un JWT est un jeton autoporté composé de trois parties encodées en base64url et séparées par des points : un header, un payload (les claims, par exemple l'identifiant utilisateur et l'expiration) et une signature. Le serveur signe le jeton à la connexion ; ensuite il lui suffit de vérifier la signature pour faire confiance au contenu, sans rien stocker.
- Sans état : toute l'information nécessaire est dans le jeton, le serveur n'a pas besoin de mémoire partagée.
- Vérifiable partout : n'importe quel service connaissant la clé peut valider le jeton, pratique pour les architectures distribuées et le SSO.
- Outils : vous pouvez inspecter un jeton avec notre décodeur JWT, contrôler sa signature avec le vérificateur JWT ou en forger un avec le générateur JWT.
Tableau comparatif
| Critère | Session serveur | JWT |
|---|---|---|
| État | Stateful (stocké serveur) | Stateless (porté par le client) |
| Stockage serveur | Requis (Redis, base) | Aucun |
| Révocation | Immédiate | Difficile avant expiration |
| Scalabilité horizontale | Store partagé nécessaire | Native |
| Taille transmise | Petite (un identifiant) | Plus grande (claims signés) |
| Cross-domaine / SSO | Contraignant | Adapté |
| Surface XSS | Faible si cookie HttpOnly | Élevée si stocké en localStorage |
Sécurité : XSS, CSRF et révocation
Les deux approches sont sûres si elles sont bien implémentées, mais leurs risques diffèrent.
- XSS : un cookie de session
HttpOnlyest inaccessible au JavaScript, donc protégé du vol par injection. Un JWT stocké enlocalStorageest en revanche lisible par tout script, ce qui en fait une cible de choix. Stocker le JWT dans un cookieHttpOnlyannule cet avantage du JWT mais réintroduit le risque CSRF. - CSRF : les cookies sont envoyés automatiquement, donc vulnérables au CSRF sans protection (attribut
SameSite, jeton anti-CSRF). Un JWT envoyé manuellement dans l'en-têteAuthorizationn'est pas concerné. - Révocation : c'est le point faible du JWT. Comme il est autoporté, on ne peut pas l'invalider avant son expiration sans réintroduire un état serveur (liste de révocation, blacklist). Une session se supprime instantanément.
Scalabilité et architecture
Sur un seul serveur, les sessions sont triviales. Dès que vous répartissez la charge sur plusieurs instances, chaque instance doit accéder aux sessions : il faut un store partagé (Redis) ou des sticky sessions. Le JWT brille ici, car n'importe quelle instance valide le jeton sans appel réseau ni stockage commun.
- Microservices : un JWT propage l'identité d'un service à l'autre sans base centrale.
- API publiques et mobiles : le JWT évite la gestion de cookies côté client natif.
- Monolithe classique : la session reste plus simple et plus sûre par défaut.
Quand choisir l'un ou l'autre
Choisir les sessions quand
- Vous développez une application web classique avec rendu serveur
- La révocation immédiate est critique (banque, santé, back-office)
- Vous voulez la solution la plus sûre par défaut, avec le moins de pièges
- Votre infrastructure tient sur un store de sessions partagé sans douleur
Choisir le JWT quand
- Vous exposez une API consommée par des SPA, du mobile ou des tiers
- Vous avez une architecture microservices ou du SSO entre domaines
- Vous devez monter en charge horizontalement sans store partagé
- Vous acceptez de gérer l'expiration courte et le rafraîchissement des jetons
Recommandation
Pour la majorité des applications web, les sessions serveur restent le choix le plus sûr et le plus simple : révocation immédiate, cookie HttpOnly et zéro gestion de jeton côté client. Réservez le JWT aux cas où son absence d'état apporte une vraie valeur : API stateless, mobile, microservices, SSO.
Si vous optez pour le JWT, gardez une durée de vie courte (quelques minutes) couplée à un refresh token stocké en cookie HttpOnly, et prévoyez une liste de révocation pour les cas sensibles. Vous combinez alors le meilleur des deux mondes.
Questions fréquentes
Un JWT est-il chiffré ?
Non, par défaut un JWT est seulement signé, pas chiffré. Son payload est encodé en base64url et lisible par quiconque l'intercepte. Ne placez jamais de données sensibles en clair dans un JWT. Pour chiffrer le contenu, il faut recourir à JWE (JSON Web Encryption).
Où stocker un JWT côté client ?
Le plus sûr est un cookie HttpOnly, Secure et SameSite, qui protège du vol par XSS. Le localStorage est plus simple mais expose le jeton à tout script malveillant. Évitez-le pour des jetons à fort privilège.
Comment déconnecter un utilisateur avec un JWT ?
Comme le jeton est autoporté, la déconnexion réelle exige soit d'attendre son expiration, soit de tenir une liste de révocation côté serveur. C'est pourquoi on utilise des durées de vie courtes et un refresh token que l'on peut, lui, révoquer.
Peut-on combiner sessions et JWT ?
Oui, c'est une pratique courante : un access token JWT à courte durée de vie pour les appels d'API, et un refresh token géré comme une session (stocké et révocable côté serveur) pour renouveler l'access token.