Quand une API indique token expired, jwt expired ou invalid_token, je commence par confirmer que le JWT est réellement hors de sa fenêtre temporelle. Je distingue expiration, clock skew, règle supplémentaire du serveur et 401 générique causé par la signature, l’issuer ou l’audience.
J’ouvre d’abord JWT Expiry Checker pour lire exp, iat et nbf en UTC.
Étape 1 : interpréter exp en secondes
{
"iat": 1789200000,
"exp": 1789203600
}
JWT NumericDate utilise des secondes. JavaScript Date.now() utilise des millisecondes :
const expiresAtMs = payload.exp * 1000;
const expired = Date.now() >= expiresAtMs;
Mélanger ces unités produit un faux diagnostic immédiat.
Étape 2 : comparer avec l’horloge du verifier
L’horloge pertinente est celle du composant qui rejette le token : API, gateway ou middleware.
exp 12:00:00Z
API clock 12:00:06Z
clock leeway 5s
=> expired
Quelques secondes peuvent relever du skew ; quarante minutes non.
Étape 3 : vérifier nbf, iat et les règles d’âge
Le token peut échouer avant exp si :
nbfest encore futur ;iatest anormalement futur ;- l’application impose un âge maximal ;
- une session/revocation policy invalide plus tôt.
JWT Validator est plus adapté lorsqu’il faut combiner signature et claims.
Étape 4 : interpréter invalid_token avec prudence
RFC 6750 utilise invalid_token pour plusieurs situations. Je recherche donc des détails :
401 Unauthorized
invalid_token
TokenExpiredError
token expired
signature has expired
token not active
Si logs et timestamps concordent, l’expiration est confirmée. Sinon je change d’hypothèse.
Étape 5 : choisir refresh ou reauthentication
Je ne modifie pas exp. Toute modification du payload invalide la signature existante.
| Situation | Action |
|---|---|
| Access Token expiré + refresh valide | obtenir un nouvel Access Token |
| refresh expiré/révoqué | reauthentication |
| Client Credentials | demander un nouveau token |
| pas de refresh flow | relancer le flux autorisé |
exp encore futur | chercher une autre validation |
Attention aux refresh concurrents
Plusieurs requêtes peuvent détecter simultanément l’expiration et lancer un refresh. Si les refresh tokens sont rotatifs, la seconde tentative peut réutiliser un refresh token déjà consommé.
Je coordonne donc le refresh côté client, mets à jour les credentials de manière atomique et empêche les requêtes parallèles de continuer avec l’ancien Access Token.
Un JWT expiré peut avoir une signature valide
signature valid ✓
issuer valid ✓
audience valid ✓
expiration valid ✕
Il n’y a aucune contradiction : la signature et la fenêtre temporelle sont deux règles distinctes. Pour isoler la première, j’utilise JWT Signature Verifier.
Si exp est absent
Tous les profils JWT ne rendent pas exp obligatoire. Je vérifie le profil et la politique applicative.
Pour un access token court, l’absence d’expiration est un signal de policy important, mais ce n’est pas automatiquement une erreur de syntaxe JWT.
Si le token semble expirer trop tôt
- synchronisation de l’API/gateway ;
- horloge de l’identity provider ;
- seconds vs milliseconds ;
- clock tolerance ;
- maximum token age ;
- mauvais token/environment en cache ;
- revocation/session policy.
Une grande tolérance ne remplace pas des horloges correctes.
Mon workflow de récupération
- J’ouvre JWT Expiry Checker.
- Je confirme
exp,iat,nbfcomme NumericDate en secondes. - Je compare
expà l’horloge du verifier réel. - Je lis
WWW-Authenticateet les logs disponibles. - Je distingue expiration des autres causes de
invalid_token. - En cas d’expiration, j’utilise refresh ou reauthentication.
- Je ne modifie pas
expet ne réutilise pas l’ancienne signature. - Si le token est encore actif, je vérifie signature,
issetaudavec JWT Debugger.
Expiré ne signifie pas corrompu : un JWT peut être parfaitement formé et correctement signé mais se trouver hors de sa fenêtre d’acceptation.
Je documente la décision temporelle
Pour chaque refus, je note exp, l’heure UTC du verifier, la tolérance effectivement appliquée et le composant qui a pris la décision. Je peux ensuite comparer une requête qui fonctionne et une requête rejetée sans exposer le token dans un ticket. Cette trace distingue une vraie expiration d’une dérive d’horloge, d’une erreur d’unité ou d’une policy de durée maximale.