Les jetons JWT expliqués : Authentification pour les applications web modernes

· 12 min de lecture

Table des matières

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

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"
}

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 :

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

  1. Connexion utilisateur : Le client envoie les identifiants (nom d'utilisateur/mot de passe) au point de terminaison d'authentification
  2. Vérification des identifiants : Le serveur valide les identifiants par rapport à la base de données
  3. Génération du jeton : Lors d'une authentification réussie, le serveur crée un JWT contenant les informations utilisateur et les revendications
  4. Livraison du jeton : Le serveur renvoie le JWT au client (généralement dans le corps de la réponse)
  5. Stockage du jeton : Le client stocke le JWT (en mémoire, localStorage ou cookies httpOnly)
  6. Requêtes authentifiées : Pour les requêtes suivantes, le client inclut le JWT dans l'en-tête Authorization
  7. Vérification du jeton : Le serveur valide la signature JWT et vérifie l'expiration
  8. 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

Quand utiliser les sessions

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.

We use cookies for analytics. By continuing, you agree to our Privacy Policy.