← Retour au Blog

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 Team16 min de lecturesecurity
B

Big JSON Team

Technical Writer

Expert in JSON data manipulation, API development, and web technologies. Passionate about creating tools that make developers' lives easier.

16 min de lecture

# 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
  • Header (en-tête)
  • Payload (données)
  • Signature
  • 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

  • Utiliser HTTPS - Toujours
  • Expiration courte - 15 min à 1h
  • Refresh tokens - Pour renouveler
  • Secret fort - 256+ bits
  • Algorithmes explicites - Pas de "none"
  • Validation stricte - iss, aud, exp
  • Blacklist - Pour révocation
  • Rate limiting - Prévenir brute force
  • 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 !

    Share:

    Articles Connexes

    Read in English