Un JSON Web Token est un token compact et sûr pour les URL qui transporte des claims JSON signés, permettant de vérifier les requêtes sans session stockée. C’est le cadrage même de la RFC 7519 — “un moyen compact et sûr pour les URL de représenter des claims (revendications) à transférer entre deux parties” — et les deux idées qu’il contient portent tout ce qui suit : le token contient ses faits, et une signature rend ces faits vérifiables par quiconque détient la bonne clé, sans base de données dans la boucle.
Points clés
| Question | Réponse |
|---|---|
| La forme | header.payload.signature — trois parties Base64Url reliées par des points |
| L’astuce | N’importe qui peut le décoder ; seuls les détenteurs de la clé peuvent le forger |
| Pas du chiffrement | Le payload est lisible — signé ≠ secret (c’est le rôle de JWE) |
| Le compromis | Vérification sans état ↔ aucune révocation intégrée avant exp |
| La discipline | Figer les algorithmes · valider iss/aud/exp · durées de vie courtes + rotation des refresh tokens |
Un JWT, décodé
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 . eyJzdWIiOiJ1c3ItOGZrMiIsInJvbGUi… . dBjftJeZ4CVP…
header { "alg": "HS256", "typ": "JWT" }
payload { "sub": "usr-8fk2", "role": "editor",
"iss": "https://api.example.com", "aud": "example-web",
"iat": 1767024900, "exp": 1767025800 } ← durée de vie : 15 minutes
signature HMACSHA256( base64url(header) + "." + base64url(payload), secret )
N'importe qui peut DÉCODER les deux premières parties — Base64Url est un emballage, pas du chiffrement.
Seuls les détenteurs de la clé peuvent FORGER la troisième — et c'est toute l'astuce.
Le contraste qui vaut la peine d’être vu en code — l’alternative avec état à laquelle chaque décision JWT se mesure :
// JavaScript / Node.js — Back4app JS SDK
// The stateful contrast: Back4app issues revocable session tokens
const user = await Parse.User.logIn('ada', 'correct-horse-battery');
const token = user.getSessionToken(); // r:abc123… — opaque, server-side state
// Every request presents it; the server can revoke it instantly:
await Parse.User.logOut(); // token invalid NOW — no waiting for an exp claim // Flutter / Dart — Back4app Flutter SDK
// The stateful contrast: Back4app issues revocable session tokens
final user = ParseUser('ada', 'correct-horse-battery', null);
await user.login();
final token = user.sessionToken; // r:abc123… — opaque, server-side state
// Every request presents it; the server can revoke it instantly:
await user.logout(); // token invalid NOW — no waiting for an exp claim // iOS / Swift — Back4app Swift SDK
// The stateful contrast: Back4app issues revocable session tokens
let user = try await User.login(username: "ada", password: "correct-horse-battery")
let token = user.sessionToken // r:abc123… — opaque, server-side state
// Every request presents it; the server can revoke it instantly:
try await User.logout() // token invalid NOW — no waiting for an exp claim // Android / Kotlin — Back4app Android SDK
// The stateful contrast: Back4app issues revocable session tokens
val user = ParseUser.logIn("ada", "correct-horse-battery")
val token = user.sessionToken // r:abc123… — opaque, server-side state
// Every request presents it; the server can revoke it instantly:
ParseUser.logOut() // token invalid NOW — no waiting for an exp claim Comment fonctionne la vérification
Une précision que les explications sautent : “JWT” nomme le format des claims ; ce que tout le monde fait réellement circuler est un JWS (RFC 7515) — la sérialisation signée — tandis que JWE est le frère chiffré, pour les payloads qui doivent rester illisibles. Et la vérification, ce sont deux tâches, pas une : contrôler la signature, puis valider les claims — exp et nbf contre l’horloge, iss contre votre liste d’émetteurs autorisés, aud contre l’identifiant de ce service. Un token parfaitement signé mais expiré, issu du mauvais émetteur ou frappé pour une autre audience est une attaque parfaitement signée.
Les claims : le vocabulaire du payload
| Claim | Nom | Tâche du vérificateur |
|---|---|---|
iss | Émetteur (issuer) | Est-ce un émetteur en qui j’ai confiance ? |
sub | Sujet (subject) | De qui s’agit-il — l’ID utilisateur stable |
aud | Audience | A-t-il été frappé pour moi ? Rejeter les tokens des autres |
exp | Expiration | Rejeter après ce timestamp Unix |
iat / nbf | Émis à / pas avant | Vérifier la cohérence de la fenêtre de validité |
jti | ID du token | Identifiant unique — la prise dont une denylist a besoin |
| personnalisés | Rôles, tenant, plan… | Sémantique de l’app — minimale, jamais secrète |
Gardez les payloads légers, et ce pour deux raisons : les tokens voyagent à chaque requête dans les headers (les cookies plafonnent autour de 4 Ko, et chaque claim est de la bande passante répétée), et tout ce qu’ils contiennent est lisible par quiconque détient le token.
JWT vs. tokens de session
| JWT (sans état) | Token de session (avec état) | |
|---|---|---|
| Le token est | L’état lui-même, signé | Un pointeur opaque vers l’état du serveur |
| Coût par requête | Contrôle de signature, aucune recherche | Une recherche dans le magasin de sessions |
| Révocation | Aucune avant exp — par conception | Instantanée — supprimez la session |
| Auth inter-services | Tout service détenant la clé vérifie | Les services doivent partager le magasin de sessions |
| Se déconnecter signifie | Le client jette le token ; il reste valide | Le token meurt réellement |
| Meilleur foyer | API, microservices, vérification par des tiers | Apps à backend unique, sessions sensibles |
La vérité démodée par laquelle plusieurs guides de bonnes pratiques commencent désormais : pour une app rendue côté serveur avec un seul backend, les sessions du framework sont plus simples et plus contrôlables — les JWT gagnent leur place quand les tokens doivent être vérifiés entre services ou par des parties qui ne devraient pas rappeler l’émetteur à chaque requête.
Le problème de la révocation, honnêtement
L’absence d’état (statelessness) est l’impossibilité de révoquer — la même propriété, décrite deux fois. Un token signé est valide jusqu’à exp quoi qu’il se soit passé depuis : déconnexion, changement de mot de passe, bannissement du compte. Chaque correctif réintroduit de l’état, alors choisissez la saveur : une denylist indexée sur jti + iss (la construction recommandée par l’OWASP) consultée à chaque requête — peu d’état, mais de l’état ; le versionnage des clés, qui révoque tout le monde d’un coup ; ou l’architecture standard — des access tokens à courte durée de vie (5 à 15 minutes) plus des refresh tokens révocables avec rotation : chaque rafraîchissement émet un nouveau refresh token et retire l’ancien, si bien qu’un token volé meurt au premier rejeu, et la réutilisation d’un token retiré signale le vol et tue toute la famille de tokens. La latence de révocation devient alors égale à la durée de vie de l’access token — la vraie raison pour laquelle ces durées sont courtes.
Choisir un algorithme — et les attaques contre ce choix
| HS256 (HMAC) | RS256 (RSA) | Ed25519 / ES256 | |
|---|---|---|---|
| Clés | Un secret partagé | La privée signe, la publique vérifie | La privée signe, la publique vérifie |
| Les vérificateurs ont besoin de | Le secret (qui permet aussi de forger !) | La clé publique seulement | La clé publique seulement |
| Convient à | Émetteur = vérificateur, une seule partie | Multi-services, tiers | Idem, signatures plus petites et plus rapides |
| Arête vive | Les secrets courts se cassent par force brute hors ligne à partir d’un seul token capturé — utilisez ≥ 256 bits aléatoires | Tokens plus gros, plus lent | Support des bibliothèques plus récent |
La section attaques pour laquelle la RFC 8725 existe, en trois phrases. alg: "none" est une valeur de header légale — les bibliothèques qui l’honorent acceptent des tokens non signés. L’attaque par confusion : un vérificateur qui laisse le header du token choisir l’algorithme peut se voir remettre un token “HS256” signé avec la clé RSA publique du serveur utilisée comme secret HMAC — une valeur publique employée comme valeur privée. Les deux meurent de la même façon : le vérificateur fige ses algorithmes et ses clés acceptés dans la configuration et ne fait jamais confiance au header pour choisir.
Où stocker les JWT dans un navigateur
| Emplacement | XSS le vole ? | CSRF l’envoie ? | Survit au rechargement ? | Verdict |
|---|---|---|---|---|
| localStorage | Oui | Non | Oui | À éviter — lisible par les scripts |
| Cookie simple | Oui (lisible par les scripts) | Oui | Oui | Le pire des deux |
| Cookie HttpOnly + Secure + SameSite | Non | Atténué par SameSite | Oui | Bien — pour le refresh token |
| En mémoire | Seulement pendant l’injection | Non | Non | Bien — pour l’access token |
| Le backend-for-frontend détient les tokens | Le navigateur ne les a jamais | Les règles des cookies s’appliquent | Oui | Le plus solide pour les SPA |
Le modèle de menace, pas le folklore : localStorage échange l’immunité au CSRF contre le vol par XSS, les cookies échangent l’inverse — c’est pourquoi le consensus scinde la paire : access token en mémoire, refresh token dans un cookie durci.
Cas d’usage courants
- Authentification d’API — le bearer token derrière les headers
Authorizationdes API REST et GraphQL. - Identité en microservices — un token émis par la gateway et vérifié indépendamment par chaque service, sans magasin de sessions partagé.
- ID tokens OpenID Connect — l’assertion d’identité signée de chaque connexion sociale — toujours un JWT.
- Passages de relais entre systèmes — liens signés, payloads de webhooks, autorisations de téléchargement : des claims vérifiés par une partie qui ne peut pas vous rappeler.
- Indices d’autorisation sans état — rôles et ID de tenant transportés dans les claims, le contrôle faisant foi restant côté serveur.
Devriez-vous utiliser des JWT ou des sessions ? Matrice de décision
| Votre situation | Choisissez |
|---|---|
| Backend unique, app rendue côté serveur | Les sessions — plus simples, révocables instantanément |
| API publique consommée par de nombreux services | Les JWT, avec clés asymétriques |
| Microservices derrière une gateway | Les JWT — vérification locale, pas de magasin partagé |
| Le verrouillage instantané est une exigence ferme | Les sessions, ou JWT + denylist et exp court |
| Des tiers doivent vérifier vos assertions | Les JWT — c’est le terrain de jeu du format |
| App mobile face à un BaaS | Le mécanisme de session de la plateforme — elle a déjà choisi |
Limites et trade-offs
- L’irrévocabilité est structurelle. Chaque atténuation — denylists, durées courtes, rotation — est un retour partiel de l’état que vous aviez retiré ; évaluez-en le coût avant de choisir le sans-état.
- Les claims se périment. Les rôles sont figés à l’émission ; un admin rétrogradé reste admin jusqu’à l’expiration. Les durées de vie courtes bornent la fenêtre de péremption.
- Le payload est public. Traitez-le comme lisible par l’utilisateur et par tout voleur de token — des identifiants oui, des secrets et des données personnelles non.
- Les réglages par défaut des bibliothèques ont piégé des équipes. Figer les algorithmes, valider les claims et contrôler
typsont des responsabilités de votre configuration, pas des garanties de la bibliothèque. - La taille s’accumule. Chaque claim voyage à chaque requête ; les payloads généreux taxent la bande passante mobile et peuvent déborder les limites des cookies.
Les tokens sur Back4app
Back4app est une plateforme open-source de Backend as a Service (BaaS) qui combine une base de données gérée, des API REST et GraphQL générées automatiquement, l’authentification, le stockage de fichiers et des fonctions serverless avec Cloud Code. Le choix de la plateforme elle-même illustre la matrice de décision : les sessions client utilisent des tokens de session révocables, côté serveur — les onglets de code ci-dessus — si bien que la déconnexion, les changements de mot de passe et la clôture de session depuis le dashboard prennent effet immédiatement, sans fenêtre d’expiration à attendre. Les JWT apparaissent là où ils ont leur place : les ID tokens OIDC des fournisseurs d’identité sont vérifiés côté serveur par les adaptateurs d’auth lors de la connexion sociale, et les fonctions Cloud Code peuvent frapper ou vérifier des JWT pour des passages de relais avec des tiers à l’aide de bibliothèques standard — avec le secret de signature dans la configuration côté serveur, jamais livré aux clients. Avec état là où le contrôle compte, sans état en bordure là où la vérification doit voyager : l’architecture que cet article défend, préassemblée.
Questions fréquentes
Qu'est-ce qu'un JWT en termes simples ?
Une "carte d'identité" JSON signée et encodée en Base64 qu'un serveur émet à la connexion. Le client la présente à chaque requête, et le serveur vérifie la signature au lieu de chercher une session — le token lui-même porte qui vous êtes et jusqu'à quand. Selon la RFC qui le définit, la prononciation suggérée est celle du mot anglais "jot".
Quelles sont les trois parties d'un JWT ?
Le header (type de token et algorithme de signature), le payload (les claims — les données JSON proprement dites) et la signature, chacun encodé en Base64Url et reliés par des points en header.payload.signature. La signature est calculée sur les deux premières parties, donc toute altération de celles-ci casse la vérification.
Un JWT est-il chiffré ?
Non — encodé, pas chiffré. Base64Url est un format de transport que n'importe qui peut inverser ; collez n'importe quel JWT dans un décodeur et le payload se lit en clair. La signature empêche l'altération, pas la lecture. Ne mettez jamais de secrets ni de données personnelles sensibles dans le payload d'un JWT ; la variante chiffrée (JWE) existe pour les vrais besoins de confidentialité.
Quelle est la différence entre un JWT et un token de session ?
L'endroit où vit l'état. Un token de session est un ID opaque qui pointe vers un état côté serveur — une recherche par requête, révocable instantanément. Un JWT porte l'état en lui-même — aucune recherche, vérifiable par tout service détenant la clé, mais valide jusqu'à expiration quoi qu'il arrive. Mise à l'échelle sans état contre contrôle instantané.
Où stocker un JWT dans le navigateur ?
Pas dans localStorage — n'importe quel script injecté peut le lire, ce qui transforme un XSS en vol de token. Le consensus actuel : gardez l'access token en mémoire, le refresh token dans un cookie HttpOnly, Secure et SameSite, et envisagez le pattern backend-for-frontend, qui tient les tokens entièrement à l'écart du navigateur.
Un JWT peut-il être révoqué ?
Pas par conception — un token signé est valide jusqu'à son claim exp, et c'est le prix de l'absence d'état. Les contournements réintroduisent tous de l'état : une denylist indexée sur le claim jti, des clés versionnées ou — la réponse standard — des access tokens à durée de vie très courte associés à des refresh tokens révocables avec rotation.
Quelle est la différence entre HS256 et RS256 ?
La symétrie. HS256 signe et vérifie avec un seul secret partagé — acceptable seulement quand émetteur et vérificateur sont la même partie, et seulement avec un secret long et aléatoire. RS256 (et le plus récent Ed25519) signe avec une clé privée tandis que n'importe qui vérifie avec la clé publique — le choix par défaut dès que plus d'un service contrôle les tokens.
Quelle est la différence entre JWT et OAuth ?
Des catégories différentes : JWT est un format de token ; OAuth 2.0 est un framework d'autorisation. Ils se composent — les flux OAuth émettent couramment des access tokens qui se trouvent être des JWT, et les ID tokens d'OpenID Connect en sont toujours. L'un dit comment un token est construit ; l'autre, comment les tokens sont distribués.