Un JWT (JSON Web Token) est une chaîne en trois parties base64url, header.payload.signature, qu'un serveur remet à l'utilisateur à la connexion et vérifie à chaque requête suivante. La signature prouve que le serveur a émis le token et que personne ne l'a modifié, mais le payload est seulement encodé, pas chiffré, donc tout le monde peut le lire. En Node.js, vous en créez un avec new SignJWT(payload).sign(secret) du package jose et vous le vérifiez avec jwtVerify(token, secret).
Le programme affiche 3 parts, puis user-42 admin : jwtVerify a vérifié la signature et l'expiration, puis a renvoyé le payload. Changez d'un caractère le secret passé à jwtVerify et relancez : la vérification échoue avec signature verification failed.
Ce que contient un JWT
Le header et le payload sont du JSON encodé en base64url. Les décoder ne demande aucune clé, c'est pourquoi un JWT ne doit jamais contenir de mot de passe ni rien d'autre de secret.
Les deux premières lignes affichent {"alg":"HS256"} et {"name":"Ada","role":"editor"}, lisibles par quiconque détient le token. La signature (43 caractères pour HS256) est ce qui empêche de le modifier : remplacez editor par admin et la signature ne correspond plus.
| Partie | Contient | Secret ? |
|---|---|---|
| Header | L'algorithme, comme HS256 | Non, lisible |
| Payload | Les claims : sub (id utilisateur), exp (expiration), iat (date d'émission), vos propres champs | Non, lisible |
| Signature | Signature HMAC ou RSA des deux premières parties | Il faut la clé pour la créer |
Expiration et vérification qui échoue
setExpirationTime('15m') écrit un claim exp, et jwtVerify rejette un token qui l'a dépassé. Chaque rejet est une erreur avec un code, ce qui permet de distinguer un token expiré d'un token falsifié.
La première vérification échoue avec ERR_JWT_EXPIRED et la seconde, dont la signature a été modifiée, avec ERR_JWS_SIGNATURE_VERIFICATION_FAILED. Pour votre API, les deux veulent dire la même chose (répondre 401), mais journaliser le code aide au débogage.
Protéger une route Express avec un Bearer token
Le client se connecte une fois, garde le token et l'envoie dans l'en-tête Authorization sous la forme Bearer <token>. Un middleware le vérifie et place le payload dans req.user pour la route.
La première requête, GET /me sans token, reçoit 401 et Missing token. Envoyez POST /login avec le corps {"username": "ada", "password": "lovelace"}, copiez le token de la réponse, puis renvoyez GET /me avec l'en-tête Authorization: Bearer <token> : la réponse est {"id":"user-1","name":"ada"}.
Le package jsonwebtoken
jsonwebtoken (npm install jsonwebtoken) est l'autre choix courant. Ses fonctions sign et verify sont synchrones (avec des callbacks optionnels) au lieu de renvoyer des promesses :
const jwt = require('jsonwebtoken');
const token = jwt.sign({ role: 'admin' }, process.env.JWT_SECRET, {
subject: 'user-42',
expiresIn: '1h',
});
const payload = jwt.verify(token, process.env.JWT_SECRET); // throws if invalid
Utilisez jose pour du nouveau code : il n'a aucune dépendance et tourne aussi dans les edge runtimes et les navigateurs. Les tokens des deux bibliothèques sont des JWT standard, donc l'une peut vérifier ce que l'autre a signé.
Erreurs fréquentes
Utiliser decode au lieu de verify. jwt.decode et decodeJwt lisent le payload sans vérifier la signature, donc un token falsifié passe. Ne faites confiance qu'à ce que renvoie verify.
Un secret court ou commité. Un secret HS256 devinable permet à un attaquant de signer ses propres tokens. Utilisez au moins 32 octets aléatoires, lisez-le depuis process.env et ne le commitez jamais.
Des tokens qui n'expirent jamais. Sans exp, un token qui fuite fonctionne pour toujours. Définissez toujours une expiration.
Questions fréquentes
Où le client doit-il stocker un JWT ?
Dans une application navigateur, un cookie HttpOnly et Secure est le plus sûr, car les scripts de la page ne peuvent pas le lire. localStorage est plus simple, mais n'importe quel script de la page, y compris un script injecté, peut lire le token. Les applications mobiles utilisent le stockage sécurisé de la plateforme.
Comment déconnecter un utilisateur avec des JWT ?
Le serveur ne peut pas annuler un token qu'il a déjà signé ; il reste valide jusqu'à son expiration. Supprimez-le côté client, gardez des access tokens de courte durée, et si vous avez besoin d'une déconnexion immédiate, tenez côté serveur une liste des identifiants de tokens révoqués (jti) et vérifiez-la à chaque requête.
HS256 ou RS256 ?
HS256 utilise un seul secret partagé pour signer et vérifier, ce qui convient à un backend unique. RS256 signe avec une clé privée et vérifie avec une clé publique, donc d'autres services peuvent vérifier les tokens sans pouvoir en créer.
Combien de temps un JWT doit-il rester valide ?
Les access tokens sont en général valides de 5 à 60 minutes. Pour des sessions plus longues, associez-les à un refresh token stocké avec plus de précautions, que le client échange contre un nouvel access token.