Face à token expired, jwt expired ou un 401 qui semble lié au temps, je vérifie exp tôt mais jamais seul. Je convertis le timestamp, le compare à l’horloge du verifier puis lis aussi iat et nbf.
Pour une vérification rapide, j’utilise JWT Expiry Checker.
exp est une NumericDate en secondes
{
"iat": 1789200000,
"nbf": 1789200000,
"exp": 1789203600
}
Conceptuellement :
current verifier time < exp
Lorsque l’horloge du verifier atteint ou dépasse exp, un profil qui applique l’expiration doit rejeter le token, sous réserve d’une petite leeway explicite.
Le piège secondes / millisecondes
const now = Math.floor(Date.now() / 1000);
const expired = typeof payload.exp === "number" && now >= payload.exp;
Ou :
const expired = Date.now() >= payload.exp * 1000;
Je n’effectue jamais Date.now() >= payload.exp sans conversion.
Même contrôle en Python
import time
if isinstance(payload.get("exp"), (int, float)) and time.time() >= payload["exp"]:
raise ValueError("token expired")
Ces snippets illustrent le temps ; ils ne remplacent pas la vérification de signature et des autres claims.
Clock skew : petite marge, pas extension arbitraire
exp 12:00:00Z
verifier clock 12:00:03Z
leeway 5s
Quelques secondes peuvent être plausibles. Un token expiré depuis 45 minutes ne redevient pas valide grâce au skew.
Je lis iat, nbf et exp ensemble
issued -------- active window -------- expired
iat nbf exp
Signaux anormaux :
nbf > exp;iattrès futur ;exp < iat;- lifetime beaucoup trop long ;
- timestamps multipliés/divisés par 1000.
JWT Validator me permet de combiner cette lecture avec signature, issuer et audience.
Je vérifie l’horloge du composant qui rejette réellement
Dans une architecture distribuée, l’horloge pertinente peut être celle du gateway, d’un edge worker ou d’un service backend. Je compare donc explicitement UTC côté verifier :
date -u +%s
et, si l’environnement est conteneurisé, je vérifie aussi que les nœuds utilisent bien la synchronisation de temps attendue. Un navigateur juste n’empêche pas un pod mal synchronisé de produire des 401.
Exemple de validation explicite côté serveur
Le but est d’avoir une policy lisible plutôt qu’une tolérance implicite dispersée dans le code :
const now = Math.floor(Date.now() / 1000);
const leeway = 5;
if (typeof payload.nbf === "number" && now + leeway < payload.nbf) {
throw new Error("token not active");
}
if (typeof payload.exp === "number" && now - leeway >= payload.exp) {
throw new Error("token expired");
}
En production, je préfère laisser la bibliothèque JWT effectuer ces contrôles après vérification cryptographique, mais cet exemple rend la logique de temps visible lors d’un diagnostic.
exp absent dépend du profil
RFC 7519 définit le claim mais les profils décident de son caractère obligatoire. RFC 7523 impose par exemple exp dans son propre contexte.
Je demande donc : type de token, profil, policy de l’API et âge maximal attendu.
exp futur mais token rejeté
Je passe à d’autres causes :
- signature invalide ;
nbffutur ;issincorrect ;audincorrect ;- mauvais token type ;
- scopes/roles ;
- revocation/session policy.
Dans ce cas j’utilise JWT Debugger.
Modifier exp ne renouvelle pas la signature
Changer le timestamp change les octets du payload. L’ancienne signature doit échouer. Le renouvellement correct produit un nouveau JWT signé.
J’observe les erreurs de temps sans journaliser les tokens
Des métriques simples suffisent souvent :
- nombre de
expiredpar route ; - nombre de
not_active; - distribution de
exp - nowau moment du rejet ; - écart d’horloge détecté entre nœuds ;
- fréquence des refresh juste avant expiration.
Je peux enregistrer request ID, catégorie d’erreur et timestamps normalisés sans stocker le JWT complet.
Tests négatifs utiles
exp = now + 60 -> accept si le reste passe
exp = now - 60 -> reject expired
nbf = now + 60 -> reject not active
exp en millisecondes-> détecter/configurer l’erreur d’unité
exp < iat -> signaler incohérence
exp absent -> appliquer la policy du profil
Ces tests évitent qu’un changement de leeway ou d’unité ne transforme silencieusement la fenêtre de validité.
Matrice de diagnostic
| Observation | Interprétation | Étape suivante |
|---|---|---|
now >= exp | expiration probable | refresh/reauthentication |
| écart de quelques secondes | skew possible | clocks/leeway |
nbf > now | pas encore actif | issuance/clock |
iat futur | clock/unité | infrastructure |
exp absent | policy de profil | spécification |
exp futur + 401 | autre validation | signature/iss/aud |
Mon workflow exp
- Je décode avec JWT Decoder.
- Je confirme que
expest numérique et en secondes. - Je convertis en UTC.
- Je compare à l’horloge du verifier réel.
- Je vérifie
iatetnbf. - Je contrôle une clock tolerance explicite.
- Je vérifie si le profil exige
exp. - Je reproduis le cas avec un test proche de la frontière temporelle.
- En cas d’expiration, j’utilise le mécanisme de renouvellement prévu.
- Sinon je passe à signature, issuer et audience.
exp répond à la question « le token est-il hors de sa fenêtre temporelle ? », pas à la question complète de confiance.