Les jetons JWT expliqués : Authentification pour les applications web modernes
· 12 min de lecture
Table des matières
- Qu'est-ce qu'un JWT ?
- Structure détaillée d'un JWT
- Comment fonctionne l'authentification JWT
- JWT vs Authentification basée sur les sessions
- Implémenter JWT dans votre application
- Meilleures pratiques de sécurité
- Vulnérabilités courantes et comment les prévenir
- Stratégies de rafraîchissement des jetons
- Où stocker les JWT
- Considérations de performance
- Questions fréquemment posées
- Articles connexes
Les jetons Web JSON (JWT) sont devenus la norme de facto pour l'authentification dans les applications web modernes. Ils fournissent un moyen compact et sûr pour les URL de représenter des revendications entre deux parties, permettant une authentification sans état qui évolue horizontalement sans stockage de session partagé.
Que vous construisiez une API REST, une architecture de microservices ou une application monopage, comprendre les JWT est essentiel pour implémenter une authentification sécurisée et évolutive. Ce guide complet explique comment fonctionnent les JWT, leur structure, les considérations de sécurité et les meilleures pratiques prêtes pour la production.
Qu'est-ce qu'un JWT ?
JWT (prononcé « jotte ») est une norme ouverte (RFC 7519) qui définit un format compact et autonome pour transmettre en toute sécurité des informations entre les parties sous forme d'objet JSON. Les informations sont signées numériquement en utilisant soit une clé secrète (HMAC) soit une paire de clés publique/privée (RSA ou ECDSA), garantissant l'intégrité et l'authenticité du jeton.
Contrairement à l'authentification traditionnelle basée sur les sessions où le serveur maintient l'état de session en mémoire ou dans une base de données, les JWT sont sans état. Toutes les informations nécessaires pour vérifier l'identité de l'utilisateur sont contenues dans le jeton lui-même.
Cas d'utilisation principaux pour JWT
- Authentification : Après qu'un utilisateur se connecte, le serveur émet un JWT que le client inclut avec les requêtes suivantes pour prouver son identité
- Autorisation : Les JWT peuvent contenir des rôles utilisateur, des permissions et des portées, permettant aux serveurs de prendre des décisions de contrôle d'accès granulaires sans recherches en base de données
- Échange d'informations : La nature signée des JWT garantit que l'expéditeur est bien celui qu'il prétend être et que les données n'ont pas été altérées pendant la transmission
- Authentification unique (SSO) : Les JWT permettent une authentification transparente sur plusieurs domaines et services
- Authentification API : Les applications mobiles et les intégrations tierces peuvent authentifier les requêtes API sans maintenir de sessions côté serveur
Conseil pro : Utilisez notre Décodeur JWT pour inspecter et valider les jetons pendant le développement. Il vous aide à comprendre la structure et à détecter les problèmes courants avant qu'ils n'atteignent la production.
Structure détaillée d'un JWT
Un JWT se compose de trois parties distinctes séparées par des points (.) : en-tête.charge_utile.signature
Voici à quoi ressemble un JWT complet :
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkphbmUgRGV2ZWxvcGVyIiwiZW1haWwiOiJqYW5lQGV4YW1wbGUuY29tIiwicm9sZSI6ImFkbWluIiwiaWF0IjoxNzEwNTkwNDAwLCJleHAiOjE3MTA2NzY4MDB9.4Adcj0mYZ8s5vxjKvV8pF7jKX9s8vZ5xJ3kL9mN2pQ4
Partie 1 : En-tête
L'en-tête contient généralement deux champs qui décrivent les opérations cryptographiques du jeton :
{
"alg": "HS256",
"typ": "JWT"
}
algspécifie l'algorithme de signature (HS256, RS256, ES256, etc.)typdéclare le type de jeton, qui est toujours « JWT »
Cet objet JSON est encodé en Base64Url pour former la première partie du JWT. L'encodage le rend sûr pour les URL et compact pour la transmission dans les en-têtes HTTP.
Partie 2 : Charge utile
La charge utile contient des revendications — des déclarations sur l'utilisateur et des métadonnées supplémentaires. Les revendications sont classées en trois types :
{
"sub": "1234567890",
"name": "Jane Developer",
"email": "[email protected]",
"role": "admin",
"iat": 1710590400,
"exp": 1710676800
}
Les revendications enregistrées sont prédéfinies par la spécification JWT et fournissent des informations standard :
iss(émetteur) : Identifie qui a émis le jetonsub(sujet) : Identifie le sujet du jeton (généralement l'ID utilisateur)aud(audience) : Identifie les destinataires prévusexp(temps d'expiration) : Horodatage Unix lorsque le jeton expirenbf(pas avant) : Le jeton n'est pas valide avant cette heureiat(émis à) : Quand le jeton a été crééjti(ID JWT) : Identifiant unique pour le jeton
Les revendications publiques sont des revendications personnalisées qui doivent être définies dans le registre IANA JSON Web Token ou utiliser des noms résistants aux collisions (comme des URL).
Les revendications privées sont des revendications personnalisées convenues entre les parties, comme role, permissions ou organizationId.
Important : La charge utile est seulement encodée en Base64Url, pas chiffrée. Ne stockez jamais d'informations sensibles comme des mots de passe, des numéros de carte de crédit ou des numéros de sécurité sociale dans une charge utile JWT.
Partie 3 : Signature
La signature garantit que le jeton n'a pas été altéré. Elle est créée en prenant l'en-tête encodé, la charge utile encodée, une clé secrète et en appliquant l'algorithme spécifié dans l'en-tête :
HMACSHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload),
secret
)
La signature permet au destinataire de vérifier que l'expéditeur est bien celui qu'il prétend être et que le message n'a pas été modifié en cours de route.
Comparaison des algorithmes de signature
| Algorithme | Type | Niveau de sécurité | Cas d'utilisation | Gestion des clés |
|---|---|---|---|---|
| HS256 | Symétrique (HMAC) | Bon | Service unique, API internes | Secret partagé |
| RS256 | Asymétrique (RSA) | Très bon | Services multiples, API publiques | Paire de clés publique/privée |
| ES256 | Asymétrique (ECDSA) | Excellent | Exigences de haute sécurité | Paire de clés publique/privée |
| PS256 | Asymétrique (RSA-PSS) | Excellent | Applications modernes | Paire de clés publique/privée |
Comment fonctionne l'authentification JWT
Comprendre le flux d'authentification complet vous aide à implémenter correctement les JWT et à résoudre efficacement les problèmes.
Le flux d'authentification complet
- Connexion utilisateur : Le client envoie les identifiants (nom d'utilisateur/mot de passe) au point de terminaison d'authentification
- Vérification des identifiants : Le serveur valide les identifiants par rapport à la base de données
- Génération du jeton : Lors d'une authentification réussie, le serveur crée un JWT contenant les informations utilisateur et les revendications
- Livraison du jeton : Le serveur renvoie le JWT au client (généralement dans le corps de la réponse)
- Stockage du jeton : Le client stocke le JWT (en mémoire, localStorage ou cookies httpOnly)
- Requêtes authentifiées : Pour les requêtes suivantes, le client inclut le JWT dans l'en-tête Authorization
- Vérification du jeton : Le serveur valide la signature JWT et vérifie l'expiration
- Accès accordé : Si valide, le serveur traite la requête en utilisant les revendications dans le jeton
Exemple de requête d'authentification
POST /api/auth/login HTTP/1.1
Host: api.example.com
Content-Type: application/json
{
"email": "[email protected]",
"password": "securePassword123"
}
Exemple de réponse d'authentification
HTTP/1.1 200 OK
Content-Type: application/json
{
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"expiresIn": 3600,
"tokenType": "Bearer"
}
Exemple de requête API authentifiée
GET /api/users/profile HTTP/1.1
Host: api.example.com
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
JWT vs Authentification basée sur les sessions
Choisir entre JWT et l'authentification basée sur les sessions dépend de l'architecture, de l'échelle et des exigences de votre application.
| Aspect | JWT | Basé sur les sessions |
|---|---|---|
| État | Sans état (le serveur ne stocke pas les jetons) | Avec état (le serveur stocke les données de session) |
| Évolutivité | Excellente (pas de stockage partagé nécessaire) | Nécessite un magasin de sessions (Redis, base de données) |
| Révocation | Difficile (nécessite une liste noire ou une expiration courte) | Facile (supprimer la session du magasin) |
| Taille | Plus grande (envoyée avec chaque requête) | Plus petite (juste l'ID de session) |
| Inter-domaines | Facile (compatible CORS) | Complexe (restrictions de domaine des cookies) |
| Applications mobiles | Idéal (pas besoin de support des cookies) | Difficile (gestion des cookies) |
| Sécurité | Nécessite une implémentation soigneuse | Modèles bien établis |
Quand utiliser JWT
- Construction de microservices qui doivent partager l'authentification
- Développement d'applications mobiles ou de SPA
- Création d'API publiques pour des intégrations tierces
- Implémentation de l'authentification unique (SSO) sur plusieurs domaines
- Évolution horizontale sans stockage de session partagé
Quand utiliser les sessions
- Construction d'applications web traditionnelles rendues côté serveur
- Nécessité de capacités de révocation immédiate des jetons
- Travail avec des données sensibles nécessitant un contrôle côté serveur
- Minimisation de la bande passante (les sessions utilisent des cookies plus petits)
- Implémentation de fonctionnalités complexes de gestion de session
Implémenter JWT dans votre application
Parcourons des exemples d'implémentation pratiques dans les langages de programmation et frameworks populaires.
Node.js avec Express
const jwt = require('jsonwebtoken');
const express = require('express');
const app = express();
const SECRET_KEY = process.env.JWT_SECRET;
// Point de terminaison de connexion
app.post('/api/auth/login', async (req, res) => {
const { email, password } = req.body;
// Vérifier les identifiants (pseudo-code)
const user = await verifyCredentials(email, password);
if (!user) {
return res.status(401).json({ error: 'Identifiants invalides' });
}
// Générer JWT
const token = jwt.sign(
{
sub: user.id,
email: user.email,
role: user.role
},
SECRET_KEY,
{ expiresIn: '1h' }
);
res.json({ accessToken: token });
});
// Middleware d'authentification
const authenticateToken = (req, res, next) => {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1];
if (!token) {
return res.status(401).json({ error: 'Aucun jeton fourni' });
}
jwt.verify(token, SECRET_KEY, (err, user) => {
if (err) {
return res.status(403).json({ error: 'Jeton invalide' });
}
req.user = user;
next();
});
};
// Route protégée
app.get('/api/users/profile', authenticateToken, (req, res) => {
res.json({ user: req.user });
});
Python avec Flask
from flask import Flask, request, jsonify
import jwt
import datetime
import os
app = Flask(__name__)
SECRET_KEY = os.getenv('JWT_SECRET')
@app.route('/api/auth/login', methods=['POST'])
def login():
data = request.get_json()
email = data.get('email')
password = data.get('password')
# Vérifier les identifiants
user = verify_credentials(email, password)
if not user:
return jsonify({'error': 'Identifiants invalides'}), 401
# Générer JWT
token = jwt.encode({
'sub': user['id'],
'email': user['email'],
'role': user['role'],
'exp': datetime.datetime.utcnow() + datetime.timedelta(hours=1)
}, SECRET_KEY, algorithm='HS256')
return jsonify({'accessToken': token})
def token_required(f):
def decorator(*args, **kwargs):
token = request.headers.get('Authorization')
if not token:
return jsonify({'error': 'Aucun jeton fourni'}), 401
try:
token = token.split(' ')[1]
data = jwt.decode(token, SECRET_KEY, algorithms=['HS256'])
request.user = data
except jwt.ExpiredSignatureError:
return jsonify({'error': 'Jeton expiré'}), 403
except jwt.InvalidTokenError:
return jsonify({'error': 'Jeton invalide'}), 403
return f(*args, **kwargs)
decorator.__name__ = f.__name__
return decorator
@app.route('/api/users/profile')
@token_required
def profile():
return jsonify({'user': request.user})
Conseil rapide : Utilisez toujours des variables d'environnement pour les clés secrètes. Ne les codez jamais en dur dans votre code source et ne les validez pas dans le contrôle de version. Utilisez des outils comme Générateur de mots de passe pour créer des secrets forts.