JWT (JSON Web Token)
- sécurité
- authentification
- api
Le JSON Web Token est le standard dominant pour l'authentification stateless. Au lieu de stocker une session sur le serveur et de la chercher en base à chaque requête, le JWT contient toute l'information nécessaire directement dans le token. Le serveur n'a besoin de rien stocker — il vérifie la signature et fait confiance au contenu.
La structure : trois parties
Un JWT est composé de trois parties séparées par des points :
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6Ik5pY29sYXMiLCJyb2xlIjoiYWRtaW4iLCJpYXQiOjE3MDk0NTUyMDB9.
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
Chaque partie est encodée en Base64URL (pas chiffré — juste encodé).
1. Header
{
"alg": "HS256",
"typ": "JWT"
}L'algorithme de signature utilisé (HS256, RS256, ES256) et le type de token.
2. Payload
{
"sub": "1234567890",
"name": "Nicolas",
"role": "admin",
"iat": 1709455200,
"exp": 1709458800
}Les claims — les données transportées par le token. Les claims standards :
sub(subject) — identifiant de l'utilisateuriat(issued at) — date de créationexp(expiration) — date d'expirationiss(issuer) — émetteur du tokenaud(audience) — destinataire prévu
Vous pouvez ajouter des claims personnalisés (role, permissions, etc.).
3. Signature
HMACSHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload),
secret
)
La signature garantit l'intégrité du token. Si quelqu'un modifie le payload (par exemple, changer role: "user" en role: "admin"), la signature ne correspondra plus et le serveur rejettera le token.
Le flux d'authentification
1. LOGIN
Client → POST /auth/login { email, password }
Serveur → Vérifie les credentials
Serveur → Génère un JWT signé avec le secret
Serveur → Retourne le JWT au client
2. REQUÊTES AUTHENTIFIÉES
Client → GET /api/users
Header: Authorization: Bearer eyJhbG...
Serveur → Vérifie la signature du JWT
Serveur → Extrait les claims (userId, role)
Serveur → Traite la requête (pas besoin de base de données)
3. EXPIRATION
Client → GET /api/users (token expiré)
Serveur → 401 Unauthorized
Le serveur n'a aucune session en mémoire. Il ne consulte aucune base de données pour vérifier le token. Il vérifie juste la signature avec son secret — c'est du stateless pur.
Implémentation
import jwt from "jsonwebtoken";
const SECRET = process.env.JWT_SECRET;
// Génération du token
function generateToken(user: User): string {
return jwt.sign(
{ sub: user.id, role: user.role },
SECRET,
{ expiresIn: "15m" }
);
}
// Middleware de vérification
function authMiddleware(req: Request, res: Response, next: NextFunction) {
const header = req.headers.authorization;
if (!header?.startsWith("Bearer ")) {
return res.status(401).json({ error: "Token manquant" });
}
const token = header.slice(7);
try {
const payload = jwt.verify(token, SECRET);
req.user = payload;
next();
} catch (err) {
return res.status(401).json({ error: "Token invalide" });
}
}HS256 vs RS256
Deux familles d'algorithmes :
| HS256 (symétrique) | RS256 (asymétrique) | |
|---|---|---|
| Clé | Un seul secret partagé | Clé privée + clé publique |
| Signature | Le même secret signe et vérifie | La clé privée signe, la publique vérifie |
| Cas d'usage | Un seul service (monolithe) | Microservices, tiers |
| Avantage | Simple | Les services de vérification n'ont pas besoin du secret |
En architecture microservices, RS256 est préférable : seul le service d'authentification détient la clé privée. Tous les autres services vérifient les tokens avec la clé publique, sans risque de compromission du secret.
Le piège n°1 : "JWT = chiffré"
Un JWT n'est PAS chiffré. Le payload est encodé en Base64, pas chiffré. N'importe qui peut le décoder :
echo "eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6Ik5pY29sYXMifQ" | base64 -d
# {"sub":"1234567890","name":"Nicolas"}Ne stockez JAMAIS de données sensibles dans un JWT : mots de passe, numéros de carte bancaire, données personnelles. Le JWT est signé (personne ne peut le modifier), mais pas confidentiel (tout le monde peut le lire).
Si vous avez besoin de confidentialité, utilisez JWE (JSON Web Encryption) — mais dans la majorité des cas, il suffit de ne pas mettre de données sensibles dans le token.
Les autres erreurs courantes
Ne pas vérifier l'algorithme. L'attaque "algorithm none" : un attaquant crée un JWT avec "alg": "none" et aucune signature. Si le serveur accepte les tokens sans vérifier l'algorithme attendu, il accepte n'importe quoi.
// ✅ Toujours spécifier l'algorithme attendu
jwt.verify(token, SECRET, { algorithms: ["HS256"] });Des tokens trop longs en durée de vie. Un JWT expirant dans 30 jours est un risque : il ne peut pas être révoqué facilement (stateless = pas de liste noire côté serveur). Préférez des durées courtes (5-15 minutes) avec un Refresh Token.
Stocker le JWT dans le localStorage. Le localStorage est accessible par n'importe quel script JavaScript — une faille XSS suffit pour voler le token. Préférez un cookie httpOnly ou la mémoire JavaScript.
Mettre trop de données dans le payload. Le JWT est envoyé à chaque requête dans le header HTTP. Un JWT de 4KB ajouté à chaque requête, c'est du bandwidth gaspillé. Gardez le payload minimal : identifiant et rôle.
Le JWT est un outil puissant pour l'authentification stateless — pas de session serveur, pas de requête en base à chaque appel. Mais il vient avec des responsabilités : durée de vie courte, stockage sécurisé, vérification stricte de l'algorithme, et surtout — ne jamais oublier qu'un JWT est lisible par tous.