JSON Web Tokens (JWT) : Authentification et sécurité expliquées
Guide complet des JSON Web Tokens : structure, signature, validation, sécurité. Implémentez l'authentification JWT dans vos applications.
Big JSON Team
• Technical WriterExpert in JSON data manipulation, API development, and web technologies. Passionate about creating tools that make developers' lives easier.
# JSON Web Tokens (JWT) : Authentification et sécurité expliquées
Les JWT sont le standard pour l'authentification moderne. Ce guide explique leur structure, fonctionnement et implémentation sécurisée.
Qu'est-ce qu'un JWT ?
Un JWT (JSON Web Token) est un token compact et autosuffisant pour transmettre des informations entre parties de manière sécurisée.
Structure
Un JWT se compose de 3 parties séparées par des points :
xxxxx.yyyyy.zzzzz
Exemple complet
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
Anatomie d'un JWT
1. Header
{
"alg": "HS256",
"typ": "JWT"
}
- alg : Algorithme de signature (HS256, RS256, etc.)
- typ : Type de token (toujours "JWT")
Encodé en Base64URL : eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
2. Payload
{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022,
"exp": 1516242622
}
Claims standard :
- sub (subject) : Identifiant utilisateur
- iat (issued at) : Date d'émission (timestamp)
- exp (expiration) : Date d'expiration
- iss (issuer) : Émetteur du token
- aud (audience) : Destinataire
Encodé en Base64URL : eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ
3. Signature
HMACSHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload),
secret
)
Résultat : SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
Implémentation
Node.js (jsonwebtoken)
Installation
npm install jsonwebtoken
Créer un token
const jwt = require('jsonwebtoken');
const SECRET = 'votre-secret-tres-securise';
// Créer token
const payload = {
userId: 123,
username: 'alice',
role: 'admin'
};
const token = jwt.sign(payload, SECRET, {
expiresIn: '1h', // Expire dans 1 heure
issuer: 'myapp',
audience: 'api'
});
console.log(token);
Vérifier un token
try {
const decoded = jwt.verify(token, SECRET);
console.log('Token valide:', decoded);
// { userId: 123, username: 'alice', role: 'admin', ... }
} catch (error) {
if (error.name === 'TokenExpiredError') {
console.error('Token expiré');
} else if (error.name === 'JsonWebTokenError') {
console.error('Token invalide');
}
}
Middleware Express
function authenticateToken(req, res, next) {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1]; // Bearer TOKEN
if (!token) {
return res.status(401).json({ error: 'Token manquant' });
}
jwt.verify(token, SECRET, (err, user) => {
if (err) {
return res.status(403).json({ error: 'Token invalide' });
}
req.user = user;
next();
});
}
// Usage
app.get('/api/protected', authenticateToken, (req, res) => {
res.json({ message: 'Accès autorisé', user: req.user });
});
Python (PyJWT)
Installation
pip install pyjwt
Créer et vérifier
import jwt
import datetime
SECRET = 'votre-secret-tres-securise'
# Créer token
payload = {
'user_id': 123,
'username': 'alice',
'exp': datetime.datetime.utcnow() + datetime.timedelta(hours=1)
}
token = jwt.encode(payload, SECRET, algorithm='HS256')
print(token)
# Vérifier token
try:
decoded = jwt.decode(token, SECRET, algorithms=['HS256'])
print('Token valide:', decoded)
except jwt.ExpiredSignatureError:
print('Token expiré')
except jwt.InvalidTokenError:
print('Token invalide')
Flask middleware
from flask import Flask, request, jsonify
from functools import wraps
app = Flask(__name__)
def token_required(f):
@wraps(f)
def decorated(args, kwargs):
token = request.headers.get('Authorization')
if not token:
return jsonify({'error': 'Token manquant'}), 401
try:
# Retirer "Bearer "
token = token.split()[1]
data = jwt.decode(token, SECRET, algorithms=['HS256'])
current_user = data['user_id']
except:
return jsonify({'error': 'Token invalide'}), 403
return f(current_user, args, kwargs)
return decorated
@app.route('/api/protected')
@token_required
def protected(current_user):
return jsonify({'message': 'Accès autorisé', 'user': current_user})
Flux d'authentification
1. Login
// Client envoie credentials
POST /api/login
{
"username": "alice",
"password": "secret123"
}
// Serveur vérifie et retourne JWT
{
"token": "eyJhbGciOiJIUzI1NiIs...",
"expiresIn": 3600
}
2. Requêtes authentifiées
// Client envoie token dans header
GET /api/profile
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
// Serveur vérifie et répond
{
"id": 123,
"username": "alice",
"email": "alice@example.com"
}
3. Refresh token
// Système avec access + refresh tokens
const accessToken = jwt.sign(payload, SECRET, { expiresIn: '15m' });
const refreshToken = jwt.sign(payload, REFRESH_SECRET, { expiresIn: '7d' });
// Stocker refresh token en DB
await saveRefreshToken(userId, refreshToken);
// Client utilise refresh pour obtenir nouveau access
POST /api/refresh
{
"refreshToken": "..."
}
// Retourne nouveau access token
{
"accessToken": "...",
"expiresIn": 900
}
Algorithmes de signature
Symmetric (HMAC)
HS256, HS384, HS512
// Même secret pour signer et vérifier
const token = jwt.sign(payload, SECRET, { algorithm: 'HS256' });
jwt.verify(token, SECRET);
Avantages : Rapide, simple
Inconvénients : Secret partagé
Asymmetric (RSA, ECDSA)
RS256, RS384, RS512, ES256
const fs = require('fs');
// Clé privée pour signer
const privateKey = fs.readFileSync('private.key');
const token = jwt.sign(payload, privateKey, { algorithm: 'RS256' });
// Clé publique pour vérifier
const publicKey = fs.readFileSync('public.key');
jwt.verify(token, publicKey);
Avantages : Plus sécurisé, clé publique partageable
Inconvénients : Plus lent
Générer clés RSA
# Clé privée
openssl genrsa -out private.key 2048
# Clé publique
openssl rsa -in private.key -pubout -out public.key
Sécurité
⚠️ Failles courantes
1. Secret faible
// ❌ MAUVAIS
const SECRET = 'secret';
// ✅ BON
const SECRET = process.env.JWT_SECRET; // 256+ bits aléatoires
2. Pas d'expiration
// ❌ MAUVAIS - token éternel
const token = jwt.sign(payload, SECRET);
// ✅ BON
const token = jwt.sign(payload, SECRET, { expiresIn: '1h' });
3. Données sensibles
// ❌ MAUVAIS - payload visible
const payload = {
userId: 123,
password: 'secret123', // JAMAIS !
creditCard: '1234-5678-9012-3456' // JAMAIS !
};
// ✅ BON
const payload = {
userId: 123,
role: 'admin'
};
4. Algorithm "none"
// ❌ Attaque possible
jwt.verify(token, SECRET); // Accepte "none" algorithm
// ✅ Spécifier algorithmes autorisés
jwt.verify(token, SECRET, { algorithms: ['HS256'] });
Bonnes pratiques
Stockage côté client
// ✅ localStorage (simple mais vulnérable XSS)
localStorage.setItem('token', token);
// ✅✅ httpOnly cookie (plus sécurisé)
res.cookie('token', token, {
httpOnly: true, // Pas accessible JavaScript
secure: true, // HTTPS uniquement
sameSite: 'strict', // CSRF protection
maxAge: 3600000 // 1 heure
});
// ❌ Ne jamais stocker dans URL
Révocation de tokens
Blacklist
const blacklist = new Set();
function revokeToken(token) {
blacklist.add(token);
}
function isTokenRevoked(token) {
return blacklist.has(token);
}
// Middleware
function authenticateToken(req, res, next) {
const token = req.headers.authorization?.split(' ')[1];
if (isTokenRevoked(token)) {
return res.status(403).json({ error: 'Token révoqué' });
}
// ... vérification normale
}
Token versioning
// Inclure version dans payload
const payload = {
userId: 123,
tokenVersion: user.tokenVersion
};
// Incrémenter version lors logout
await User.update({ id: 123 }, { tokenVersion: user.tokenVersion + 1 });
// Vérifier version
if (decoded.tokenVersion !== user.tokenVersion) {
throw new Error('Token obsolète');
}
Debugging
Décoder JWT
jwt.io
Coller token sur https://jwt.io pour voir contenu.
CLI
# Décoder header
echo "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9" | base64 -d
# Node.js
node -e "console.log(JSON.parse(Buffer.from('eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9', 'base64')))"
JavaScript
function decodeJWT(token) {
const parts = token.split('.');
return {
header: JSON.parse(atob(parts[0])),
payload: JSON.parse(atob(parts[1]))
};
}
Alternatives
- Opaque tokens - Random string + DB lookup
- Sessions - Server-side storage
- OAuth 2.0 - Standard complet
- PASETO - Alternative plus sécurisée
Conclusion
JWT en résumé :
- Autosuffisant et stateless
- Structure : Header.Payload.Signature
- Idéal pour APIs et microservices
- Attention sécurité (secret, expiration, HTTPS)
Cas d'usage :**
- ✅ APIs REST
- ✅ Single Sign-On (SSO)
- ✅ Microservices
- ✅ Mobile apps
- ❌ Sessions longues (utiliser refresh)
Implémentez JWT correctement pour une authentification sécurisée !
Articles Connexes
JavaScript et JSON : Guide complet de manipulation des données
Maîtrisez JSON en JavaScript avec JSON.parse(), JSON.stringify(), et techniques avancées. Guide pratique avec exemples pour le développement web moderne.
Comprendre JSON Schema : Validation et documentation des données
Maîtrisez JSON Schema pour valider vos données JSON. Guide complet avec exemples pratiques, types, validations et outils pour créer des schémas robustes.
API JSON et services REST : Guide complet du développeur
Maîtrisez les API REST avec JSON. Apprenez les méthodes HTTP, l'authentification, la gestion des erreurs et les meilleures pratiques pour créer des API modernes.