Comment décoder et déboguer un JWT
Un JSON Web Token se compose simplement de trois parties encodées en Base64URL. Une fois que vous savez les lire, la plupart des erreurs « invalid token » s’expliquent en quelques secondes. Ce guide vous montre comment décoder un JWT, vérifier les claims qui posent le plus souvent problème, et éviter les erreurs qui transforment les JWT en failles de sécurité.
De quoi est composé un JWT
Un JWT ressemble à xxxxx.yyyyy.zzzzz. Les trois parties, séparées par des points, sont :
- En-tête (Header) — du JSON contenant l’algorithme de signature (
alg) et le type de jeton, par exemple{"alg":"HS256","typ":"JWT"}. - Charge utile (Payload) — du JSON contenant les claims : à qui le jeton est destiné et à quelle période il est valide.
- Signature — un hash des deux premières parties, calculé avec un secret (HMAC) ou une clé privée (RSA, ECDSA).
Les deux premières parties sont uniquement encodées, et non chiffrées. Toute personne qui détient le jeton peut les lire : ne mettez donc jamais de mots de passe ou de données personnelles dans la charge utile d’un JWT.
Décoder un jeton
Collez le jeton dans le JWT Decoder : il affiche l’en-tête, la charge utile et chaque horodatage sous forme de date lisible. L’outil fonctionne dans votre navigateur, donc vos jetons de production ne sont envoyés nulle part. Pour le faire manuellement, décodez chaque partie en Base64URL — remplacez - par +, _ par /, ajoutez le padding — ou, avec Node :
const [header, payload] = token.split('.').slice(0, 2)
.map(part => JSON.parse(Buffer.from(part, 'base64url').toString()));
console.log(header, payload);
Les claims qui posent le plus souvent problème
| Claim | Signification | Erreur typique |
|---|---|---|
exp | Expiration, en secondes Unix | « jwt expired » — vérifiez aussi les horloges du serveur et du client |
nbf | Non valide avant | Un jeton utilisé quelques secondes trop tôt, car l’horloge de l’émetteur est en avance |
iat | Émis le | Rejeté comme « émis dans le futur » après une dérive d’horloge |
aud | Audience à laquelle le jeton est destiné | Un jeton prévu pour une API envoyé à une autre — le vérificateur doit le contrôler |
iss | Émetteur | Émetteurs de développement et de production confondus |
Les horodatages sont exprimés en secondes, et non en millisecondes. Si une date d’expiration correspond à l’an 55 000, quelqu’un a écrit Date.now() au lieu de Math.floor(Date.now() / 1000). Le Unix Timestamp Converter permet de le vérifier. La plupart des bibliothèques acceptent une petite tolérance d’horloge (30 à 60 secondes) pour absorber les écarts entre serveurs.
Pourquoi « invalid signature » ?
- Mauvais secret ou mauvaise clé — la cause la plus fréquente. Vérifiez la variable d’environnement du serveur qui vérifie le jeton, et pas seulement celle du serveur qui le signe.
- Mauvais algorithme — un jeton signé en RS256 vérifié en HS256, ou l’inverse.
- Le jeton a été modifié — changer un seul caractère de la charge utile casse la signature, et c’est bien le but.
- Rotation des clés — les clés de l’émetteur ont changé. Avec JWKS, faites correspondre le
kidde l’en-tête à une clé publiée.
Pour vérifier un jeton HS256 à la main, calculez le HMAC-SHA256 de header.payload avec le secret à l’aide du HMAC Generator, puis comparez-le, encodé en Base64URL, à la troisième partie.
Erreurs de sécurité à éviter
- Décoder n’est pas vérifier. Ne faites jamais confiance aux claims issus de
jwt.decode(); utilisez la fonction de vérification de votre bibliothèque avec une liste explicite d’algorithmes autorisés. - Rejetez
alg: noneet figez l’algorithme — ne laissez jamais le jeton choisir comment il est contrôlé. - Gardez des jetons de courte durée (quelques minutes pour les jetons d’accès) et utilisez des refresh tokens pour les sessions longues ; un JWT ne peut pas être révoqué avant son expiration sans état supplémentaire côté serveur.
- Ne stockez pas les jetons dans le localStorage si une faille XSS pourrait les lire ; un cookie HttpOnly, Secure, SameSite est plus sûr pour les applications web.
Besoin d’un jeton de test avec des claims précis ? Créez-le avec le JWT Encoder.