Décodeur / Encodeur JWT
—
—
{ }
Décodez et encodez vos tokens JWT en ligne : visualisez header, payload et signature avec coloration syntaxique, vérifiez l'expiration, générez un JWT signé (HS256/HS384/HS512) avec vos propres claims. 100% côté client, aucune donnée envoyée.
Qu'est-ce qu'un JWT ?
Un JSON Web Token (JWT) est un standard ouvert (RFC 7519) qui définit un moyen compact et autonome de transmettre des informations entre parties sous forme d'un objet JSON. Ces informations peuvent être vérifiées et approuvées car elles sont signées numériquement.
Structure d'un token JWT
Un JWT est composé de trois parties séparées par des points (.), chacune encodée en Base64Url :
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 ← Header (rouge)
.
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIn0 ← Payload (violet)
.
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c ← Signature (cyan)
- Header : contient le type de token (
JWT) et l'algorithme de signature (HS256,RS256…) - Payload : contient les claims — les données transportées (identité, rôles, expiration…)
- Signature : garantit l'intégrité du token — calculée avec le header, le payload et une clé secrète
Cas d'usage typiques
- ✅ Authentification stateless : le serveur n'a pas besoin de stocker de session
- ✅ API REST sécurisée : le client envoie le token dans l'en-tête
Authorization: Bearer <token> - ✅ SSO (Single Sign-On) : un token valide sur plusieurs services
- ✅ Partage d'information vérifiable : claims signés entre microservices
Les claims standards
Les claims sont les propriétés JSON transportées dans le payload. La spécification RFC 7519 définit des claims enregistrés (noms réservés) :
| Claim | Nom complet | Type | Description |
|---|---|---|---|
sub |
Subject | String | Identifiant unique du sujet (ex: ID utilisateur) |
iss |
Issuer | String | Identifiant de l'émetteur du token (ex: URL du serveur d'auth) |
aud |
Audience | String / Array | Destinataire(s) prévu(s) du token |
exp |
Expiration Time | NumericDate | Date/heure d'expiration (timestamp Unix en secondes) |
iat |
Issued At | NumericDate | Date/heure de création du token |
nbf |
Not Before | NumericDate | Le token ne doit pas être accepté avant cette date |
jti |
JWT ID | String | Identifiant unique du token (anti-rejeu) |
"role": "admin", "permissions": ["read", "write"]). Préfixez-les avec une URL pour éviter les collisions si le token est partagé entre organisations (ex: "https://monapp.com/role").
Bonnes pratiques de sécurité
Ce qu'il ne faut jamais faire
- ❌ Stocker des données sensibles dans le payload (mots de passe, tokens OAuth, données bancaires)
- ❌ Ignorer la vérification de
expcôté serveur - ❌ Accepter l'algorithme
none— certaines bibliothèques vulnérables acceptaient un token sans signature - ❌ Stocker le token dans
localStoragesans protection XSS - ❌ Utiliser un secret trop court ou prévisible pour HMAC
Recommandations
- ✅ Toujours vérifier
exp,issetaudcôté serveur avant d'accorder l'accès - ✅ Utiliser HTTPS exclusivement pour transmettre les tokens
- ✅ Préférer
RS256en production : asymétrique (clé privée signe, clé publique vérifie) — aucun secret partagé entre services - ✅ Courte durée de vie : utilisez des tokens
access_tokencourts (15min) +refresh_tokenlongue durée - ✅ Mettre en liste noire les tokens révoqués (
jti) si votre cas d'usage l'exige - ✅ Utiliser
HttpOnlycookies pour stocker le refresh_token (inaccessible en JS)
HS256 utilise un secret symétrique — la même clé sert à signer ET à vérifier. Pratique pour un seul service, risqué si plusieurs services doivent vérifier le token. RS256 utilise une paire de clés RSA : seul votre serveur d'authentification possède la clé privée, les autres services n'ont besoin que de la clé publique.
JWT dans Angular
Intercepteur HTTP pour ajouter le Bearer token
La bonne pratique Angular est d'utiliser un HttpInterceptor pour injecter automatiquement le token JWT dans chaque requête HTTP sortante :
// auth.interceptor.ts
import { Injectable } from '@angular/core';
import {
HttpInterceptor, HttpRequest,
HttpHandler, HttpEvent
} from '@angular/common/http';
import { Observable } from 'rxjs';
import { AuthService } from './auth.service';
@Injectable()
export class AuthInterceptor implements HttpInterceptor {
constructor(private auth: AuthService) {}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
// Récupère le token stocké (localStorage ou service)
const token = this.auth.getToken();
if (token) {
// Clone la requête et ajoute l'en-tête Authorization
const authReq = req.clone({
setHeaders: {
Authorization: `Bearer ${token}`
}
});
return next.handle(authReq);
}
// Pas de token — transmet la requête telle quelle
return next.handle(req);
}
}
N'oubliez pas d'enregistrer l'intercepteur dans votre module :
// app.module.ts
providers: [
{
provide: HTTP_INTERCEPTORS,
useClass: AuthInterceptor,
multi: true // Permet d'avoir plusieurs intercepteurs
}
]
Décoder le payload pour lire les rôles
Vous pouvez décoder le payload JWT côté client pour afficher des éléments conditionnels selon le rôle — sans appel serveur. N'oubliez pas que cette vérification est côté client uniquement et ne remplace pas la vérification serveur :
// auth.service.ts
export class AuthService {
// Décode le payload JWT (sans vérification de signature)
decodeToken(token: string): any | null {
try {
// Le payload est la 2ème partie (index 1) du token
const base64Url = token.split('.')[1];
// Conversion base64url → base64 standard
const base64 = base64Url
.replace(/-/g, '+')
.replace(/_/g, '/');
// Décodage UTF-8 sécurisé
const jsonPayload = decodeURIComponent(
atob(base64)
.split('')
.map(c => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2))
.join('')
);
return JSON.parse(jsonPayload);
} catch {
return null; // Token malformé
}
}
// Vérifie si l'utilisateur a un rôle spécifique
hasRole(token: string, role: string): boolean {
const payload = this.decodeToken(token);
// Supporte "role": "admin" ou "roles": ["admin", "user"]
if (!payload) return false;
if (Array.isArray(payload.roles)) return payload.roles.includes(role);
return payload.role === role;
}
// Vérifie si le token est expiré (vérification locale)
isExpired(token: string): boolean {
const payload = this.decodeToken(token);
if (!payload?.exp) return false;
// exp est en secondes, Date.now() en millisecondes
return Date.now() >= payload.exp * 1000;
}
}
bootstrapApplication() avec provideHttpClient(withInterceptors([authInterceptor])) en utilisant des intercepteurs fonctionnels (HttpInterceptorFn).