Créa-blog

#100JoursPourCoder
Projet Créa-code

Ressources pour développeur web

Théme de la semaine : L’extension WooCommerce

Créer et sécuriser vos tokens JWT en PHP – JSON Web Token

⏱️ Temps de lecture estimé : 25 minutes
Accueil PHP 8 Créer et sécuriser vos tokens JWT en PHP – JSON Web Token

Si vous avez déjà développé un site ou une application web, vous avez sûrement entendu parler des tokens JWT, pour JSON Web Token. Ces fameux petits jetons sont devenus incontournables dans le monde du développement moderne, notamment lorsqu’il s’agit de sécuriser une API, de gérer une authentification sans session, ou encore de protéger des échanges entre un client et un serveur.

Mais qu’est-ce qu’un JWT exactement ? À quoi sert-il concrètement ? Et surtout, comment l’utiliser correctement et en toute sécurité dans un projet PHP ? C’est ce que nous allons voir ensemble, pas à pas, dans ce tutoriel complet et accessible à tous, même si vous débutez.

Nous allons commencer par poser les bases pour bien comprendre ce qu’est un token JWT, puis nous apprendrons à le générer, le vérifier, le décoder, et enfin à l’utiliser dans un vrai contexte d’authentification en PHP. Vous verrez qu’une fois le principe assimilé, le fonctionnement devient très logique.

L’objectif est que, à la fin de cet tutoriel, vous soyez capable de :

  • Comprendre comment un JWT sécurise les échanges entre client et serveur ;
  • Générer et décoder un token en PHP avec une clé secrète ;
  • Mettre en place une protection simple de vos routes API ;
  • Et appliquer les bonnes pratiques de sécurité pour éviter les pièges les plus courants.

Alors, installez-vous confortablement, et voyons ensemble comment créer et sécuriser vos tokens JWT en PHP, pas à pas.

Comprendre ce qu’est un JWT

Avant de plonger dans le code, prenons un peu de recul. Si vous débutez, il est essentiel de comprendre le problème que les tokens JWT cherchent à résoudre. En d’autres termes : pourquoi a-t-on inventé ce système ?

Le problème des sessions PHP traditionnelles

Pendant longtemps, la méthode la plus courante pour gérer une connexion utilisateur sur un site PHP était d’utiliser les sessions. Le principe est simple : lorsqu’un utilisateur se connecte, le serveur crée une session et enregistre des informations comme son identifiant, son rôle ou son pseudo.

Ces données sont stockées sur le serveur (souvent dans un fichier temporaire ou en mémoire), et un petit cookie appelé PHPSESSID est envoyé au navigateur. Ce cookie permet ensuite d’identifier l’utilisateur à chaque requête.

Cette méthode fonctionne très bien pour un site classique avec peu de trafic et un seul serveur. Mais dès que l’on parle d’API, de microservices, ou d’applications réparties sur plusieurs serveurs, la gestion des sessions devient compliquée :

  • Chaque serveur doit partager les sessions avec les autres ;
  • Il faut une base de données ou un système centralisé pour synchroniser les sessions ;
  • Cela ajoute de la complexité et des points de défaillance possibles.

C’est là qu’entre en jeu le JWT (JSON Web Token).

Le principe du JSON Web Token

Un JWT est un jeton numérique sécurisé qui contient toutes les informations nécessaires à l’identification d’un utilisateur ou à la vérification d’un droit d’accès. Il est dit autoportant (self-contained), c’est-à-dire qu’il contient tout ce qu’il faut, sans avoir besoin d’accéder à une session sur le serveur.

Concrètement, quand un utilisateur s’authentifie (par exemple avec un email et un mot de passe), le serveur lui génère un token JWT et le renvoie au format texte. Ce token est ensuite stocké côté client (dans le navigateur, souvent dans le localStorage ou un cookie sécurisé). Lors de chaque requête suivante, le client renvoie ce token au serveur, qui peut le vérifier sans jamais avoir besoin d’aller consulter une base de sessions.

Le serveur sait que le token est authentique et non modifié grâce à une signature cryptographique. C’est cette signature qui rend le JWT sécurisé.

À quoi ressemble un JWT ?

Un JSON Web Token est en réalité une simple chaîne de caractères, mais avec une structure bien définie. Par exemple :

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJ1c2VySWQiOjEsImVtYWlsIjoiamVhbi5kdXBuQGVtYWlsLmNvbSIsImlhdCI6MTY5Mjg2NDA2MywiZXhwIjoxNjkyODY3NjYzfQ.
dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk

Cela peut paraître étrange au premier abord, mais il s’agit simplement de trois blocs encodés en base64, séparés par des points :

  1. Le Header (en-tête)
  2. Le Payload (corps du message)
  3. La Signature

Ces trois parties sont essentielles pour comprendre comment le JWT fonctionne, et nous les détaillerons dans le prochain chapitre.

Pourquoi utiliser un JWT plutôt qu’une session ?

Le JWT présente plusieurs avantages majeurs :

  • Il n’a pas besoin d’un stockage côté serveur. Tout est contenu dans le jeton ;
  • Il est rapide à vérifier grâce à la signature cryptographique ;
  • Il est compatible avec toutes les technologies (PHP, JavaScript, Python, etc.) ;
  • Il fonctionne parfaitement pour les API REST, où les requêtes sont souvent stateless (sans session).

En revanche, il faut être conscient que le JWT n’est pas magique. Si on le manipule mal (mauvais stockage, clé faible, absence de HTTPS), il peut vite devenir une faille de sécurité. C’est pourquoi il est crucial de comprendre sa structure et son fonctionnement interne avant de le manipuler.

Le cycle d’un JWT dans une application PHP

Pour bien visualiser comment un JWT s’insère dans une application web, prenons un petit scénario concret.

  1. L’utilisateur se connecte sur votre site avec son email et son mot de passe.
  2. Le serveur PHP vérifie ses identifiants dans la base de données.
  3. Si tout est bon, le serveur génère un JWT contenant par exemple son ID utilisateur, son email, et la date d’expiration du token.
  4. Le serveur renvoie ce JWT au navigateur.
  5. À chaque requête suivante vers une route protégée (par exemple /api/profil), le client renvoie ce JWT dans les en-têtes HTTP.
  6. Le serveur vérifie la validité du token (signature et date d’expiration).
  7. Si le token est valide, la ressource demandée est renvoyée ; sinon, une erreur 401 (“non autorisé”) est retournée.

Ce système est simple, rapide et très efficace lorsqu’il est bien implémenté.

JWT et sécurité

Ce qui rend un JWT fiable, c’est sa signature numérique. Lorsqu’un serveur crée un token, il le signe avec une clé secrète connue de lui seul. Ainsi, même si un utilisateur malveillant essaye de modifier le contenu du token (par exemple changer son rôle ou son email), la signature ne correspondra plus, et le serveur rejettera le token.

C’est pour cette raison qu’il est primordial de protéger la clé secrète et de ne jamais la rendre publique. Nous verrons plus loin comment la définir et la gérer proprement dans votre code PHP.

JWT et JSON : pourquoi ce format ?

Le “J” de JWT vient de JSON (JavaScript Object Notation), un format de données léger et universel. Il est extrêmement facile à lire, à générer et à interpréter, que ce soit en PHP, JavaScript, Python ou autre.

Un payload de JWT peut donc ressembler à ceci avant encodage :

{
  "userId": 12,
  "email": "[email protected]",
  "role": "user",
  "iat": 1692864063,
  "exp": 1692867663
}

C’est cette simplicité et cette compatibilité qui ont rendu les JWT si populaires dans le monde du développement web moderne.

Nous avons désormais une compréhension claire de ce qu’est un JWT, à quoi il sert, et pourquoi il est devenu une solution de référence pour la sécurité des API PHP. Maintenant, nous allons ouvrir le capot pour explorer la structure interne d’un JWT, et comprendre comment ses trois parties fonctionnent ensemble pour garantir sécurité et authenticité.

Structure interne d’un JWT (Header, Payload, Signature)

Maintenant que vous avez compris à quoi sert un JWT et pourquoi il est devenu une solution de référence pour l’authentification et la sécurité des applications web, il est temps d’ouvrir la boîte noire. Nous allons voir comment un token JWT est construit, quelles informations il contient, et surtout, comment chacune de ses parties contribue à sa fiabilité.

Un JWT se compose toujours de trois parties distinctes séparées par des points :

header.payload.signature

Chaque partie a un rôle bien précis. Nous allons les explorer une par une, de façon simple et imagée, pour que tout soit limpide.

Le Header – L’en-tête du JWT

Le Header, c’est un peu la carte d’identité du token. Il indique principalement le type de token et l’algorithme de signature utilisé pour garantir son intégrité.

Voici un exemple typique de header avant encodage :

{
  "alg": "HS256",
  "typ": "JWT"
}

Dans cet exemple :

  • alg signifie algorithm : c’est la méthode utilisée pour signer le token.
    Le plus courant est HS256, qui correspond à l’algorithme HMAC-SHA256.
  • typ signifie type : ici, on précise qu’il s’agit d’un JSON Web Token.

Une fois ce header encodé en Base64URL, il devient illisible pour l’œil humain, mais reste très court et léger. Par exemple, ce JSON devient :

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

Ce premier bloc, placé avant le premier point du token, permet au serveur de savoir comment vérifier la signature plus tard.

Le Payload – Le contenu du message

Le Payload (ou corps du message) contient les données que vous voulez transmettre dans votre token. C’est ici que se trouvent les informations sur l’utilisateur, ses droits, son rôle, ou toute autre donnée utile.

Voici un exemple concret de payload avant encodage :

{
  "user_id": 1,
  "email": "[email protected]",
  "role": "user",
  "iat": 1729837200,
  "exp": 1729840800
}

Regardons de plus près ces champs :

  • user_id : identifiant de l’utilisateur (utile pour le retrouver rapidement).
  • email : email de l’utilisateur (ou tout autre info que vous voulez inclure).
  • role : rôle de l’utilisateur (user, admin, etc.).
  • iat : “issued at”, c’est-à-dire la date de création du token (en timestamp Unix).
  • exp : “expiration”, la date d’expiration du token. Une fois cette date dépassée, le token devient invalide.

Les champs iat et exp ne sont pas obligatoires, mais fortement recommandés. Ils permettent de contrôler la durée de vie du token et d’éviter qu’un utilisateur conserve un accès illimité.

Une fois encodé en Base64URL, ce payload devient une chaîne compacte, par exemple :

eyJ1c2VyX2lkIjoxLCJlbWFpbCI6ImNvbnRhY3RA
Y3JlYS10cm95ZXMuZnIiLCJyb2xlIjoidXNlciIs
ImlhdCI6MTcyOTgzNzIwMCwiZXhwIjoxNzI5ODQw
ODAwfQ

La Signature – Le garde du corps du token

C’est la partie la plus importante du JWT. Elle garantit que le token n’a pas été modifié depuis sa création.

Pour la générer, le serveur :

  1. Prend le header et le payload,
  2. Les concatène avec un point (.),
  3. Et signe le tout avec une clé secrète.

La formule ressemble à ceci :

signature = HMACSHA256(
  base64UrlEncode(header) + "." + base64UrlEncode(payload),
  clé_secrète
)

C’est cette signature qui empêche toute modification frauduleuse. Si quelqu’un essaie de changer ne serait-ce qu’une lettre dans le payload, la signature générée ne correspondra plus, et le serveur rejettera immédiatement le token.

Voici un exemple de signature encodée :

dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk

La clé secrète utilisée pour signer le token doit être connue uniquement du serveur. Elle est essentielle à la sécurité du JWT. En PHP, elle est généralement stockée dans un fichier .env ou une constante du type :

define('JWT_SECRET', 'MaCléUltraSecrèteEtComplexe123!@#');

Exemple complet de JWT décodé

Pour bien comprendre comment les trois parties s’assemblent, prenons un exemple concret. Voici un JWT complet :

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJ1c2VyX2lkIjoxLCJlbWFpbCI6ImNvbnRhY3RA
Y3JlYS10cm95ZXMuZnIiLCJyb2xlIjoidXNlciIs
ImlhdCI6MTcyOTgzNzIwMCwiZXhwIjoxNzI5ODQw
ODAwfQ.
v7bHTl1Zj3gEh_mHd5-4YzWWq1d8cS9HXyNfQY8pyrA

Décomposons-le :

  1. Header : {"alg": "HS256", "typ": "JWT"}
  2. Payload : {"user_id":1,"email":"[email protected]","role":"user","iat":1729837200,"exp":1729840800}
  3. Signature : v7bHTl1Zj3gEh_mHd5-4YzWWq1d8cS9HXyNfQY8pyrA

En réalité, quand vous collez ce token sur le site jwt.io, il se décode automatiquement et vous permet de visualiser toutes ces informations. C’est un excellent outil pour tester et comprendre comment fonctionne votre JWT.

Pourquoi le format Base64URL ?

Vous avez sans doute remarqué que chaque partie du JWT est encodée en une longue suite de caractères étranges. C’est du Base64URL, une variante du classique Base64 utilisée pour rendre le token sûr à transmettre dans les URL.

Le Base64 normal peut contenir des symboles comme + ou /, qui posent problème dans certaines URLs. La version Base64URL les remplace par des caractères sûrs (- et _), afin que le token puisse être passé sans souci dans un header HTTP, un paramètre d’URL ou une requête API.

Le JWT reste donc lisible et portable dans tous les contextes web.

Les “claims” standards dans un JWT

Dans le payload, vous pouvez mettre toutes les données que vous voulez, mais certaines clés ont un sens standardisé. On les appelle des claims (revendications en français).

Voici les plus courantes :

  • iss (issuer) : identifie l’émetteur du token (par exemple, votre nom de domaine) ;
  • sub (subject) : identifie le sujet du token (souvent l’utilisateur) ;
  • aud (audience) : indique pour qui le token est destiné (ex. : votre API) ;
  • exp (expiration time) : moment où le token expire ;
  • iat (issued at) : moment de création du token ;
  • nbf (not before) : moment avant lequel le token n’est pas encore valide.

Vous n’êtes pas obligé de toutes les utiliser, mais elles donnent un cadre cohérent et facilitent la compatibilité avec d’autres systèmes.

Exemple pratique : construction manuelle d’un JWT

Pour bien comprendre, voyons ensemble comment on pourrait créer un JWT “à la main” sans utiliser de bibliothèque. C’est purement éducatif, car plus tard nous utiliserons une librairie dédiée.

<?php
$header = json_encode(['alg' => 'HS256', 'typ' => 'JWT']);
$payload = json_encode(['user_id' => 1, 'email' => '[email protected]']);

$base64UrlHeader = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($header));
$base64UrlPayload = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($payload));

$secret = 'MaCléUltraSecrète123!';
$signature = hash_hmac('sha256', $base64UrlHeader . "." . $base64UrlPayload, $secret, true);
$base64UrlSignature = str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($signature));

$jwt = $base64UrlHeader . "." . $base64UrlPayload . "." . $base64UrlSignature;

echo $jwt;

Ce code :

  1. Crée un header et un payload JSON.
  2. Les encode en Base64URL.
  3. Génère une signature HMAC-SHA256 avec une clé secrète.
  4. Assemble le tout pour former un JWT complet.

Le résultat affiché sera un vrai token que vous pouvez vérifier sur jwt.io.

Bien entendu, cette méthode fonctionne, mais elle est trop basique pour un usage en production. Nous allons voir comment utiliser une bibliothèque professionnelle et sécurisée pour créer, signer et vérifier vos tokens JWT de manière fiable et robuste.

Mais avant, récapitulons. Un JSON Web Token est donc un ensemble de trois blocs :

  • Le Header indique le type et l’algorithme ;
  • Le Payload contient les données utiles sur l’utilisateur ;
  • La Signature assure l’authenticité du token.

Le tout est encodé en Base64URL, ce qui rend le token léger, sûr et transportable.

C’est ce trio — header, payload, signature — qui permet à un JWT d’être à la fois autoportantrapide à vérifier, et sécurisé sans aucune session côté serveur.

Installer et configurer une librairie JWT en PHP

Jusqu’ici, nous avons compris la théorie du JSON Web Token, sa structure interne et son fonctionnement. Désormais, place à la pratique. Nous allons voir comment :

  1. Installer une librairie JWT fiable avec Composer,
  2. Configurer une clé secrète,
  3. Et préparer notre environnement PHP pour créer et vérifier des tokens.

3.1. Pourquoi utiliser une librairie dédiée ?

Créer un JWT “à la main”, comme nous l’avons vu précédemment, est possible.
Mais cette approche présente plusieurs limites :

  • Les signatures sont faciles à mal implémenter si vous oubliez un détail ;
  • Le code devient vite complexe à maintenir ;
  • Vous devez gérer vous-même la sécurité, les algorithmes, les exceptions, etc.

C’est pourquoi il est fortement recommandé d’utiliser une bibliothèque standard.
En PHP, firebase/php-jwt est l’une des références absolues. Elle est :

  • Stable et maintenue par des développeurs expérimentés,
  • Compatible avec les algorithmes les plus utilisés (HS256, RS256, etc.),
  • Facile à intégrer dans tout projet PHP.

Installation via Composer

La manière la plus simple d’installer cette librairie est d’utiliser Composer, le gestionnaire de dépendances de PHP.

Si vous n’avez pas encore installé Composer, faites le.

Une fois Composer installé, placez-vous à la racine de votre projet (par exemple le dossier /var/www/html/mon_projet) et exécutez la commande suivante dans votre terminal :

composer require firebase/php-jwt

Composer va automatiquement :

  • Télécharger la bibliothèque,
  • Mettre à jour votre fichier composer.json,
  • Et ajouter les fichiers nécessaires dans le dossier /vendor.

Structure du projet

Après installation, votre projet PHP devrait ressembler à ceci :

mon_projet/
│
├── vendor/
│   └── (toutes les dépendances installées)
│
├── composer.json
├── composer.lock
├── .env
└── index.php

Le fichier .env (que nous allons créer) servira à stocker votre clé secrète JWT, afin qu’elle ne soit jamais écrite directement dans le code.

Créer une clé secrète sécurisée

Comme nous l’avons vu plus haut, le secret key est un élément fondamental de la sécurité de vos tokens. C’est grâce à cette clé que le serveur peut vérifier qu’un token n’a pas été falsifié.

Évitez absolument les clés trop simples comme “12345” ou “secret”.
Une clé doit être :

  • Longue (au moins 32 caractères),
  • Composée de chiffres, lettres, majuscules, minuscules et symboles.

Vous pouvez en générer une avec PHP directement dans votre terminal :

php -r "echo base64_encode(random_bytes(64));"

Cette commande renverra une longue chaîne de caractères aléatoires, que vous pourrez copier dans votre fichier .env :

JWT_SECRET=VOTRE_CLE_TRES_LONGUE_ET_SECURISÉE_123456...

Pour des raisons de sécurité, ce fichier .env ne doit jamais être partagé ou versionné (ajoutez-le à votre .gitignore si vous utilisez Git).

Créer un fichier d’environnement et le charger

Afin d’utiliser facilement cette clé secrète dans votre code PHP, vous pouvez la charger depuis le fichier .env à l’aide d’une petite fonction.

Voici un exemple simple dans un fichier config.php :

<?php
// config.php
function getEnvValue($key) {
    $lines = file(__DIR__ . '/.env', FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
    foreach ($lines as $line) {
        list($envKey, $envValue) = explode('=', $line, 2);
        if ($envKey === $key) {
            return trim($envValue);
        }
    }
    return null;
}

define('JWT_SECRET', getEnvValue('JWT_SECRET'));

Ainsi, vous pourrez récupérer votre clé secrète depuis n’importe quel script PHP avec :

require 'config.php';
echo JWT_SECRET;

Importer la librairie dans votre projet

Maintenant que la clé secrète est prête et que la librairie est installée, il ne reste plus qu’à l’inclure dans votre code.

Pour cela, il suffit d’ajouter la ligne suivante en haut de votre fichier PHP :

require 'vendor/autoload.php';

use Firebase\JWT\JWT;
use Firebase\JWT\Key;

La ligne require 'vendor/autoload.php' permet à Composer de charger automatiquement toutes les classes nécessaires. Ensuite, grâce au mot-clé use, on importe les classes dont nous aurons besoin :

  • JWT : pour encoder et décoder les tokens ;
  • Key : pour vérifier la signature d’un token lors de la lecture.

Générer un premier token JWT en PHP

Passons à la pratique ! Voici un exemple concret pour générer un JWT valide à partir d’un utilisateur connecté :

<?php
require 'vendor/autoload.php';
require 'config.php';

use Firebase\JWT\JWT;

$payload = [
    'user_id' => 12,
    'email' => '[email protected]',
    'role' => 'user',
    'iat' => time(),
    'exp' => time() + 3600 // expiration dans 1 heure
];

$jwt = JWT::encode($payload, JWT_SECRET, 'HS256');

echo "Votre token JWT : " . $jwt;

Voici ce que fait ce code :

  • Il définit un payload avec des informations sur l’utilisateur ;
  • Il ajoute deux champs importants :
    • iat (issued at) : l’heure actuelle ;
    • exp (expiration) : l’heure d’expiration (ici, +1 heure).
  • Il encode le tout avec la clé secrète et l’algorithme HS256.

Le résultat affiché sera un token complet du type :

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJ1c2VyX2lkIjoxMiwiZW1haWwiOiJjb250YWN0QGNyZWEt
dHJveWVzLmZyIiwicm9sZSI6InVzZXIiLCJpYXQiOjE3Mjk4
Mzc0MjAsImV4cCI6MTcyOTgzOTIyMH0.
mNQ7gG0W8fhbcdm2fw4B8F4zyTrVJEm0nFyiXYAOhHk

Explication ligne par ligne

Prenons un instant pour bien comprendre ce qui se passe :

  1. require 'vendor/autoload.php'; charge les bibliothèques installées par Composer.
  2. require 'config.php'; importe la clé secrète depuis le fichier .env.
  3. Le tableau $payload contient les informations du token.
  4. JWT::encode() transforme ce tableau en un JWT signé, grâce à la clé secrète.
  5. Enfin, le token est affiché, prêt à être utilisé dans une requête HTTP.

En production, vous n’afficherez pas le token sur la page, bien sûr. Il sera plutôt renvoyé au client sous forme de réponse JSON dans une API REST, par exemple :

header('Content-Type: application/json');
echo json_encode(['token' => $jwt]);

Vérifier un token JWT en PHP

Une fois un token généré, vous devez aussi savoir le vérifier. C’est essentiel pour sécuriser vos routes protégées (nous aborderons ce point plus en détail plus tard).

Voici un exemple pour décoder et vérifier un token :

<?php
require 'vendor/autoload.php';
require 'config.php';

use Firebase\JWT\JWT;
use Firebase\JWT\Key;

$jwt = $_GET['token'] ?? ''; // Exemple : token reçu depuis l’URL

try {
    $decoded = JWT::decode($jwt, new Key(JWT_SECRET, 'HS256'));
    echo "Token valide. Utilisateur ID : " . $decoded->user_id;
} catch (Exception $e) {
    echo "Token invalide : " . $e->getMessage();
}

Ce script :

  • Récupère le token JWT (ici dans l’URL pour l’exemple),
  • Le décode avec la clé secrète,
  • Vérifie automatiquement sa signature et sa validité,
  • Attrape les erreurs éventuelles (signature invalide, token expiré, etc.).

Gestion des erreurs courantes

Lors de la vérification d’un JWT, plusieurs erreurs peuvent se produire :

  • Token expiré : si la date d’expiration (exp) est dépassée ;
  • Signature invalide : si le token a été altéré ;
  • Clé erronée : si le serveur n’utilise pas la bonne clé secrète.

La librairie firebase/php-jwt gère très bien ces cas. Elle lèvera une exception avec un message explicite, que vous pouvez intercepter avec un try/catch comme ci-dessus.

Bonnes pratiques à ce stade

  • Ne stockez jamais la clé secrète dans le code source.
  • Privilégiez les variables d’environnement et un fichier .env.
  • Ne conservez pas vos tokens trop longtemps : 1 heure ou 2 heures d’expiration, c’est l’idéal.
  • Transmettez-les toujours via HTTPS, jamais en clair.

Protéger vos routes et vos pages avec un JWT

Nous avons vu comment générer et vérifier un JSON Web Token en PHP à l’aide de la librairie firebase/php-jwt. Il est temps de voir comment l’utiliser dans une vraie logique d’authentification, comme on le ferait dans une API ou une application moderne.

L’objectif est simple :

Formation web et informatique - Alban Guillier - Formateur

Des formations informatique pour tous !

Débutant ou curieux ? Apprenez le développement web, le référencement, le webmarketing, la bureautique, à maîtriser vos appareils Apple et bien plus encore…

Formateur indépendant, professionnel du web depuis 2006, je vous accompagne pas à pas et en cours particulier, que vous soyez débutant ou que vous souhaitiez progresser. En visio, à votre rythme, et toujours avec pédagogie.

Découvrez mes formations Qui suis-je ?
  • Lorsqu’un utilisateur se connecte avec ses identifiants, le serveur génère un token JWT ;
  • Le client conserve ce token (par exemple dans le stockage local ou dans un cookie sécurisé) ;
  • À chaque requête suivante, ce token est renvoyé dans le header HTTP de la requête ;
  • Le serveur vérifie la validité du token avant d’exécuter le code protégé.

Si le token est valide, l’utilisateur accède à la ressource. Sinon, l’accès est refusé.

Un mini scénario d’authentification JWT

Imaginons que vous ayez une API PHP avec deux routes :

  • /login.php : qui permet à un utilisateur de se connecter et de recevoir un token JWT.
  • /profil.php : une page ou route protégée, accessible uniquement si le token JWT est valide.

Nous allons créer ces deux scripts et vous expliquer chaque étape.

Le script de connexion (login.php)

Commençons par le fichier login.php, qui va :

  1. Vérifier les identifiants de l’utilisateur ;
  2. Générer un token JWT s’ils sont corrects ;
  3. Renvoyer ce token au format JSON.

Voici un exemple complet :

<?php
require 'vendor/autoload.php';
require 'config.php';

use Firebase\JWT\JWT;

// Simulation d’une base de données
$utilisateurs = [
    ['email' => '[email protected]', 'password' => '123456', 'id' => 1, 'role' => 'user'],
    ['email' => '[email protected]', 'password' => 'adminpass', 'id' => 2, 'role' => 'admin']
];

// Récupération des données envoyées en JSON
$input = json_decode(file_get_contents('php://input'), true);
$email = $input['email'] ?? '';
$password = $input['password'] ?? '';

// Recherche de l’utilisateur correspondant
$user = null;
foreach ($utilisateurs as $u) {
    if ($u['email'] === $email && $u['password'] === $password) {
        $user = $u;
        break;
    }
}

header('Content-Type: application/json');

if ($user) {
    // Création du payload
    $payload = [
        'user_id' => $user['id'],
        'email' => $user['email'],
        'role' => $user['role'],
        'iat' => time(),
        'exp' => time() + 3600 // Token valable 1h
    ];

    $jwt = JWT::encode($payload, JWT_SECRET, 'HS256');

    echo json_encode([
        'status' => 'success',
        'token' => $jwt
    ]);
} else {
    http_response_code(401);
    echo json_encode(['status' => 'error', 'message' => 'Identifiants invalides']);
}

Explications :

  • Le script lit les données envoyées par le client (emailpassword) en JSON.
  • Il cherche une correspondance dans la liste d’utilisateurs.
  • Si les identifiants sont corrects :
    • Il crée un payload JWT (avec les infos du compte).
    • Il génère un token signé avec la clé secrète.
    • Il renvoie ce token au client.
  • Si les identifiants sont incorrects, il renvoie une erreur 401.

Vous pouvez tester ce script avec un outil comme PostmanInsomnia ou un simple fetch() en JavaScript :

fetch('http://localhost/login.php', {
    method: 'POST',
    headers: {'Content-Type': 'application/json'},
    body: JSON.stringify({ email: '[email protected]', password: '123456' })
})
.then(res => res.json())
.then(data => console.log(data));

Le résultat attendu :

{
  "status": "success",
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6..."
}

Le script de protection (verifyToken.php)

Pour éviter de répéter le code de vérification du token dans chaque page, il est judicieux de créer un middleware PHP, un fichier intermédiaire chargé de vérifier automatiquement le token avant d’autoriser l’accès.

Créez un fichier verifyToken.php :

<?php
require 'vendor/autoload.php';
require 'config.php';

use Firebase\JWT\JWT;
use Firebase\JWT\Key;

function verifierToken() {
    $headers = apache_request_headers();
    $authHeader = $headers['Authorization'] ?? '';

    if (!$authHeader) {
        http_response_code(401);
        echo json_encode(['error' => 'Aucun token fourni']);
        exit;
    }

    $token = str_replace('Bearer ', '', $authHeader);

    try {
        $decoded = JWT::decode($token, new Key(JWT_SECRET, 'HS256'));
        return $decoded;
    } catch (Exception $e) {
        http_response_code(401);
        echo json_encode(['error' => 'Token invalide : ' . $e->getMessage()]);
        exit;
    }
}

Ce script :

  • Récupère le header HTTP Authorization,
  • Extrait le token (après le mot “Bearer”),
  • Le décode et le vérifie,
  • Retourne les données décodées si tout va bien,
  • Sinon, il renvoie une erreur et bloque la requête.

Protéger une route (profil.php)

Maintenant, utilisons ce middleware pour protéger une page PHP :

<?php
require 'verifyToken.php';

header('Content-Type: application/json');

$user = verifierToken();

echo json_encode([
    'message' => 'Bienvenue sur votre profil !',
    'user' => [
        'id' => $user->user_id,
        'email' => $user->email,
        'role' => $user->role
    ]
]);

Ici :

  • Avant d’afficher quoi que ce soit, le script appelle verifierToken().
  • Si le token est valide, la page affiche le profil.
  • Si le token est expiré ou invalide, le middleware bloque l’accès.

Essayez d’appeler cette page sans token :

curl http://localhost/profil.php

Réponse :

{
  "error": "Aucun token fourni"
}

Puis testez avec un token valide obtenu depuis /login.php :

curl -H "Authorization: Bearer VOTRE_TOKEN_ICI" http://localhost/profil.php

Réponse :

{
  "message": "Bienvenue sur votre profil !",
  "user": {
    "id": 1,
    "email": "[email protected]",
    "role": "user"
  }
}

C’est exactement le comportement attendu !

Gérer les rôles et les droits d’accès

Vous pouvez aller plus loin et gérer les rôles (user, admin, etc.) directement via le payload JWT. Par exemple, dans profil.php, vous pouvez vérifier :

if ($user->role !== 'admin') {
    http_response_code(403);
    echo json_encode(['error' => 'Accès réservé aux administrateurs']);
    exit;
}

Cela permet d’ajouter une couche de contrôle fine sans interroger la base de données à chaque requête. Le JWT contient déjà toutes les informations nécessaires pour décider qui peut faire quoi.

Expiration et renouvellement du token

Vous vous souvenez du champ exp dans le payload ? C’est lui qui définit la durée de validité du token.

Une fois expiré, l’utilisateur devra se reconnecter, ou utiliser un système de refresh token (token secondaire permettant de régénérer un JWT sans ressaisir son mot de passe).

Le principe :

  1. Lors de la connexion, le serveur génère un token principal (access token) de courte durée (ex. 1h) et un refresh token plus long (ex. 7 jours).
  2. Quand le premier expire, le client envoie le refresh token à une route spéciale (/refresh.php) pour obtenir un nouveau JWT.
  3. Le serveur vérifie le refresh token et renvoie un nouveau token valide.

Ce mécanisme renforce la sécurité en limitant la durée d’exposition du token principal.

Où stocker le token côté client ?

Deux options principales s’offrent à vous :

  1. Le localStorage (facile à utiliser en JavaScript) :localStorage.setItem('jwt', token); Mais attention, il est vulnérable aux attaques XSS si votre site n’est pas protégé.
  2. Les cookies sécurisés (HttpOnly, SameSite) :
    C’est souvent la solution la plus sûre pour les applications web :setcookie("jwt", $jwt, time() + 3600, "/", "", true, true); Le cookie sera envoyé automatiquement avec chaque requête HTTPS sans être accessible en JavaScript.

Dans tous les cas, jamais de token dans une URL — il pourrait être enregistré dans les historiques ou les journaux du serveur.

JWT et API RESTful

Les tokens JWT sont particulièrement adaptés aux API REST, car ils rendent chaque requête stateless (sans état). Le serveur n’a pas besoin de session ou de stockage : chaque requête contient déjà toutes les informations nécessaires dans son token.

C’est ce qui rend les JWT si populaires dans les architectures modernes, notamment avec ReactVue.jsNext.jsFlutter, etc.

Résumé de cette étape

  • Le client s’authentifie → reçoit un token JWT.
  • À chaque requête, il envoie ce token dans le header Authorization.
  • Le serveur vérifie la signature et l’expiration.
  • Si le token est valide, la route s’exécute normalement.
  • Sinon, le serveur renvoie une erreur 401.

En quelques lignes de PHP, vous venez de construire un système d’authentification sécurisé, sans base de session et parfaitement adapté aux applications modernes.

Expiration, renouvellement et bonnes pratiques de sécurité JWT en PHP

Dans les chapitres précédents, vous avez appris à générer un token, à le vérifier et à protéger vos routes. Mais dans un système réel, il est indispensable de réfléchir à la durée de validité, au renouvellement automatique et à la protection des données sensibles.

Sans ces mécanismes, même le plus beau code peut devenir dangereux.

Le rôle de l’expiration dans un JWT

Chaque token JWT contient un champ optionnel mais fondamental : exp, pour expiration time. Ce champ indique la date et l’heure à laquelle le token deviendra invalide.

Lorsqu’un utilisateur se connecte, le serveur lui génère un token valable pour une durée donnée (par exemple, une heure). Une fois ce délai écoulé :

  • Le token devient invalide ;
  • Le serveur refuse son utilisation ;
  • L’utilisateur doit obtenir un nouveau token.

C’est un mécanisme de sécurité essentiel pour limiter l’impact d’un vol de token.
Même si un pirate réussit à s’en emparer, il ne pourra pas l’utiliser longtemps.

Exemple concret : définir l’expiration dans le payload

Vous avez déjà vu ce champ dans notre code précédent, mais reprenons un exemple clair :

$payload = [
    'user_id' => 1,
    'email' => '[email protected]',
    'role' => 'user',
    'iat' => time(),            // Date de création du token
    'exp' => time() + 3600      // Expiration dans 1 heure
];

Le calcul time() + 3600 correspond à 3600 secondes, soit 1 heure de validité.

Si vous souhaitez un token de 24 heures, vous pouvez utiliser :

'exp' => time() + (24 * 3600)

Lors de la vérification avec JWT::decode(), la librairie firebase/php-jwt compare automatiquement la date exp à l’heure actuelle. Si le token est expiré, une exception sera levée.

Gérer l’erreur d’expiration

Voyons un exemple de gestion propre d’un token expiré :

try {
    $decoded = JWT::decode($jwt, new Key(JWT_SECRET, 'HS256'));
    echo "Token valide, bienvenue " . $decoded->email;
} catch (\Firebase\JWT\ExpiredException $e) {
    http_response_code(401);
    echo json_encode(['error' => 'Token expiré, veuillez vous reconnecter.']);
} catch (Exception $e) {
    http_response_code(401);
    echo json_encode(['error' => 'Token invalide : ' . $e->getMessage()]);
}

Ici, on distingue le cas spécifique du token expiré pour afficher un message plus clair à l’utilisateur. Cela améliore l’expérience et permet d’implémenter ensuite un système de renouvellement automatique.

Le concept de “refresh token”

Reconnexion à chaque expiration ? Peu pratique. Pour éviter cela, on introduit un second token : le refresh token.

L’idée est simple :

  • Le JWT principal (souvent appelé access token) a une courte durée de vie (ex. : 1 heure).
  • Le refresh token a une durée plus longue (ex. : 7 jours).
  • Lorsque le premier expire, le client peut utiliser le second pour en obtenir un nouveau sans ressaisir ses identifiants.

Ce mécanisme est utilisé par tous les grands services : Google, Facebook, GitHub, etc.
Il améliore la sécurité tout en offrant un confort utilisateur.

Générer un refresh token en PHP

Lors de la connexion (dans login.php par exemple), vous pouvez générer deux tokens au lieu d’un :

use Firebase\JWT\JWT;

$accessPayload = [
    'user_id' => $user['id'],
    'email' => $user['email'],
    'role' => $user['role'],
    'iat' => time(),
    'exp' => time() + 3600 // 1 heure
];

$refreshPayload = [
    'user_id' => $user['id'],
    'iat' => time(),
    'exp' => time() + (7 * 24 * 3600) // 7 jours
];

$accessToken = JWT::encode($accessPayload, JWT_SECRET, 'HS256');
$refreshToken = JWT::encode($refreshPayload, JWT_SECRET, 'HS256');

echo json_encode([
    'access_token' => $accessToken,
    'refresh_token' => $refreshToken
]);

Le refresh token est plus simple : il ne contient pas les données sensibles, juste un identifiant. Il sert uniquement à demander un nouveau access token quand le premier expire.

Route de renouvellement du token (refresh.php)

Créez un fichier refresh.php pour gérer la régénération du token principal :

<?php
require 'vendor/autoload.php';
require 'config.php';

use Firebase\JWT\JWT;
use Firebase\JWT\Key;

header('Content-Type: application/json');

$input = json_decode(file_get_contents('php://input'), true);
$refreshToken = $input['refresh_token'] ?? '';

if (!$refreshToken) {
    http_response_code(400);
    echo json_encode(['error' => 'Aucun refresh token fourni.']);
    exit;
}

try {
    $decoded = JWT::decode($refreshToken, new Key(JWT_SECRET, 'HS256'));

    // Génération d’un nouveau token d’accès
    $newAccessPayload = [
        'user_id' => $decoded->user_id,
        'iat' => time(),
        'exp' => time() + 3600
    ];

    $newAccessToken = JWT::encode($newAccessPayload, JWT_SECRET, 'HS256');

    echo json_encode(['access_token' => $newAccessToken]);
} catch (Exception $e) {
    http_response_code(401);
    echo json_encode(['error' => 'Refresh token invalide : ' . $e->getMessage()]);
}

Ainsi, quand un access token expire, votre application peut automatiquement appeler /refresh.php avec le refresh token et recevoir un nouveau JWT valide. L’utilisateur ne voit rien, l’expérience reste fluide et sécurisée.

Où stocker le refresh token

Deux possibilités principales :

  • Côté client, dans un cookie sécurisé (HttpOnlySameSite=Strict, durée longue) ;
  • Côté serveur, dans une base de données associée à l’utilisateur.

La deuxième option est plus sûre, car vous pouvez :

  • Invalider un refresh token spécifique (en cas de déconnexion) ;
  • Suivre les connexions multiples d’un même utilisateur ;
  • Gérer une “liste noire” des tokens révoqués.

Bonnes pratiques essentielles de sécurité JWT

1. Utiliser toujours HTTPS

Ne transmettez jamais un JWT sur une connexion non chiffrée. Sans HTTPS, n’importe qui pourrait intercepter le token et l’utiliser.

2. Ne stockez pas le JWT dans le localStorage sans précaution

Le localStorage est accessible en JavaScript, donc vulnérable aux attaques XSS.
Préférez un cookie HttpOnly (non lisible en JS) et SameSite=Strict.

3. Ne mettez jamais de données sensibles dans le payload

Même si le JWT est signé, il n’est pas chiffré. Toute personne peut le décoder et lire son contenu sur jwt.io. N’y stockez donc jamais de mots de passe, d’adresses, ni de données confidentielles.

4. Utiliser des clés secrètes longues et aléatoires

La clé secrète doit être imprévisible et stockée hors du code source (fichier .env). Une clé trop simple peut être brute-forcée.

5. Définir une durée de vie courte pour les access tokens

1 à 2 heures maximum. Mieux vaut régénérer souvent un token que de risquer qu’un vol reste exploitable longtemps.

6. Rafraîchir les tokens de manière contrôlée

Ne permettez pas une régénération illimitée. Fixez une durée totale maximale d’utilisation (ex. : 7 jours) avant une reconnexion obligatoire.

7. Gérer les déconnexions proprement

Lorsqu’un utilisateur se déconnecte, supprimez ou marquez comme invalides ses refresh tokens. Vous pouvez stocker leur empreinte (hash) dans une table revoked_tokens et les vérifier avant chaque renouvellement.

8. Protéger vos endpoints avec des middlewares

Centralisez la vérification du JWT dans un fichier commun (comme verifyToken.php). Cela évite les oublis et renforce la cohérence de votre sécurité.

Exemple d’implémentation complète : login + refresh + profil

Voici un schéma global pour résumer :

  1. Login
    L’utilisateur s’authentifie → le serveur renvoie :{ "access_token": "...", "refresh_token": "..." }
  2. Requêtes protégées
    L’application envoie le header :Authorization: Bearer access_token Le serveur vérifie la signature avant d’autoriser l’accès.
  3. Token expiré
    Le client reçoit une erreur 401 Token expiré, et appelle :POST /refresh.php { "refresh_token": "..." } Le serveur renvoie un nouveau access_token.
  4. Déconnexion
    Le refresh token est supprimé côté client (ou invalidé côté serveur).

Cas avancé : JWT avec algorithmes asymétriques (RS256)

Jusqu’à présent, nous avons utilisé HS256, un algorithme symétrique : la même clé sert à signer et à vérifier. Mais dans certains contextes (microservices, API externes), il est préférable d’utiliser un algorithme asymétriquecomme RS256, basé sur une paire de clés :

  • Une clé privée (pour signer),
  • Une clé publique (pour vérifier).

Cela permet à plusieurs serveurs de vérifier la validité des tokens sans jamais détenir la clé privée.

Exemple de génération en PHP :

$privateKey = file_get_contents('private.pem');
$jwt = JWT::encode($payload, $privateKey, 'RS256');

Et pour la vérification :

$publicKey = file_get_contents('public.pem');
$decoded = JWT::decode($jwt, new Key($publicKey, 'RS256'));

Cette méthode est plus complexe à mettre en place, mais offre un niveau de sécurité professionnel, idéal pour les environnements distribués.

Les erreurs fréquentes à éviter

  • Signer le JWT avec une clé faible ou partagée publiquement
    → Risque de falsification immédiat.
  • Ne pas gérer les exceptions lors de la vérification
    → Votre script renverra des erreurs PHP visibles.
  • Conserver un token expiré en localStorage
    → L’utilisateur croit être connecté alors qu’il ne l’est plus.
  • Réutiliser le même token après déconnexion
    → Il doit toujours être invalidé côté client.

Un JWT ne remplace pas tout

Attention : un JWT n’est pas un bouclier magique. Il ne remplace pas une base de données bien protégée, ni des mots de passe hashés, ni un protocole HTTPS. C’est un outil d’authentification stateless, pas un coffre-fort.

Le JWT est efficace lorsqu’il est intégré à une architecture bien pensée :

  • Une API REST,
  • Des endpoints sécurisés,
  • Des tokens courts,
  • Des clés protégées.

Intégrer les JWT dans une architecture PHP moderne

Jusqu’ici, nous avons manipulé les JSON Web Tokens dans des fichiers PHP indépendants : login.phpprofil.phpverifyToken.php, etc. C’est parfait pour comprendre les bases. Mais dans une application réelle, il est important d’organiser le code proprement, selon une structure claire et évolutive.

Les tokens JWT s’intègrent parfaitement dans une architecture MVC (Modèle-Vue-Contrôleur) ou dans une API REST moderne. Voyons comment.

Pourquoi structurer son code

Un projet PHP bien structuré permet de :

  • Séparer la logique métier, les contrôleurs et la configuration ;
  • Réutiliser le code sans le copier ;
  • Simplifier la maintenance et l’évolution du site ;
  • Mieux sécuriser les accès sensibles.

Par exemple, votre projet pourrait ressembler à ceci :

mon_api/
│
├── app/
│   ├── controllers/
│   │   ├── AuthController.php
│   │   ├── UserController.php
│   │
│   ├── middlewares/
│   │   └── JwtMiddleware.php
│   │
│   ├── models/
│   │   └── User.php
│   │
│   └── helpers/
│       └── Response.php
│
├── public/
│   └── index.php
│
├── vendor/
│
├── config.php
├── .env
└── composer.json

Cette organisation est simple, mais déjà très efficace pour travailler avec des JWT dans une API PHP. Voyons comment chaque partie intervient.

Le middleware JWT

Le middleware, c’est le “gardien” de vos routes. Il vérifie systématiquement le token avant d’autoriser l’accès à une ressource.

Créez par exemple un fichier app/middlewares/JwtMiddleware.php :

<?php
namespace App\Middlewares;

use Firebase\JWT\JWT;
use Firebase\JWT\Key;

require_once __DIR__ . '/../../config.php';

class JwtMiddleware {
    public static function verify() {
        $headers = apache_request_headers();
        $authHeader = $headers['Authorization'] ?? '';

        if (!$authHeader) {
            self::unauthorized('Aucun token fourni');
        }

        $token = str_replace('Bearer ', '', $authHeader);

        try {
            $decoded = JWT::decode($token, new Key(JWT_SECRET, 'HS256'));
            return $decoded;
        } catch (\Firebase\JWT\ExpiredException $e) {
            self::unauthorized('Token expiré, veuillez vous reconnecter.');
        } catch (\Exception $e) {
            self::unauthorized('Token invalide : ' . $e->getMessage());
        }
    }

    private static function unauthorized($message) {
        http_response_code(401);
        echo json_encode(['error' => $message]);
        exit;
    }
}

Désormais, ce middleware pourra être appelé depuis n’importe quel contrôleur pour vérifier un accès. Cela centralise toute la logique de sécurité dans un seul fichier.

Exemple de contrôleur protégé

Prenons maintenant un contrôleur simple UserController.php :

<?php
namespace App\Controllers;

use App\Middlewares\JwtMiddleware;

class UserController {

    public function profil() {
        $user = JwtMiddleware::verify(); // Vérifie le token

        header('Content-Type: application/json');
        echo json_encode([
            'message' => 'Bienvenue sur votre profil !',
            'user' => [
                'id' => $user->user_id,
                'email' => $user->email,
                'role' => $user->role
            ]
        ]);
    }
}

Grâce à ce système, vous pouvez protéger n’importe quelle méthode simplement en appelant JwtMiddleware::verify(). Si le token est invalide ou expiré, l’accès est automatiquement bloqué.

Le contrôleur d’authentification

Votre AuthController.php gérera la connexion et la génération du token JWT :

<?php
namespace App\Controllers;

use Firebase\JWT\JWT;

require_once __DIR__ . '/../../config.php';

class AuthController {
    public function login($email, $password) {
        // Exemple simplifié, à remplacer par une vraie requête SQL
        if ($email === '[email protected]' && $password === '123456') {
            $payload = [
                'user_id' => 1,
                'email' => $email,
                'role' => 'user',
                'iat' => time(),
                'exp' => time() + 3600
            ];

            $token = JWT::encode($payload, JWT_SECRET, 'HS256');
            return json_encode(['token' => $token]);
        } else {
            http_response_code(401);
            return json_encode(['error' => 'Identifiants invalides']);
        }
    }
}

Vous pouvez ensuite appeler cette méthode depuis une route dans index.php :

<?php
require '../vendor/autoload.php';

use App\Controllers\AuthController;
use App\Controllers\UserController;

$path = $_SERVER['REQUEST_URI'];
$method = $_SERVER['REQUEST_METHOD'];

if ($path === '/login' && $method === 'POST') {
    $data = json_decode(file_get_contents('php://input'), true);
    $auth = new AuthController();
    echo $auth->login($data['email'], $data['password']);
}

if ($path === '/profil' && $method === 'GET') {
    $userController = new UserController();
    $userController->profil();
}

Vous venez de créer une mini API MVC JWT : structurée, sécurisée, et extensible.
Chaque nouveau contrôleur pourra réutiliser le middleware sans jamais répéter le code de vérification.

Intégration dans un framework PHP (bonus)

Les frameworks modernes comme LaravelSymfony ou Slim proposent déjà des systèmes de middleware. L’intégration des JWT y est encore plus simple :

  • Sous Laravel, vous pouvez utiliser le package tymon/jwt-auth.
  • Sous Slim ou Lumen, vous pouvez ajouter un middleware personnalisé basé sur le même principe que notre JwtMiddleware.

Le principe reste toujours identique :

  1. Génération du token à la connexion,
  2. Vérification du header Authorization à chaque requête,
  3. Réponse 401 en cas d’échec.

Les JWT sont universels et compatibles avec toutes les architectures modernes, ce qui les rend très puissants pour les développeurs PHP qui veulent aller plus loin.

JWT et MVC : un couple gagnant

Intégrer les JWT dans un modèle MVC permet de :

  • Gérer proprement la sécurité via des middlewares centralisés ;
  • Gagner en clarté (chaque couche a sa responsabilité) ;
  • Faciliter la scalabilité (vous pouvez ajouter d’autres routes, serveurs ou microservices sans changer le fonctionnement du token).

C’est une approche “hybride intelligente” — exactement dans la logique que tu appliques déjà sur code.crea-troyes.fr— qui combine simplicité et professionnalisme.


Les JSON Web Tokens (JWT) ont profondément transformé la manière dont les développeurs PHP gèrent la sécurité et l’authentification. Ils ont remplacé les vieilles sessions lourdes et centralisées par une approche plus souple, rapide et indépendante du serveur.

Mais pour en tirer le meilleur, il faut comprendre qu’un JWT n’est pas une session magique, ni une solution à tous les problèmes. C’est un outil de confiance, à manier avec rigueur et compréhension.

En quelques lignes de PHP, vous avez vu comment :

  • Générer et signer un JWT avec une clé secrète,
  • Vérifier sa validité et protéger vos routes,
  • Gérer l’expiration et le renouvellement,
  • L’intégrer dans une architecture MVC claire et sécurisée.

Avec ce système, vos applications PHP deviennent stateless, plus légères, plus rapides et prêtes à dialoguer avec des frontends modernes (React, Vue, Next.js ou même des applis mobiles).

C’est exactement ce type de logique que l’on retrouve dans les grandes API professionnelles — et que vous pouvez désormais appliquer à vos propres projets, du plus simple au plus ambitieux.

En suivant ces bonnes pratiques :

  • Vos données restent protégées,
  • Vos utilisateurs naviguent sans friction,
  • Et votre code reste propre et évolutif.

Vous venez de franchir un cap essentiel vers un développement PHP moderne, sécurisé et professionnel.

Chapitre 12. Challenge : Craquer un Hash