Guides JWT

Comment je vérifie le claim exp et l’expiration JWT

Je lis exp comme NumericDate, traite correctement secondes et millisecondes, compare l’horloge du verifier et clock skew puis décide si l’expiration est vraiment la cause.

Par JWTDecoder.toolsDernière vérification le 2026-09-16

Voix éditoriale : la première personne décrit le workflow documenté de JWTDecoder.tools ; JWTDecoder.tools est l’éditeur et l’auteur organisationnel.

Chronologie JWT avec iat, nbf, fenêtre active et exp
Je vérifie iat, nbf, la fenêtre active et exp avant de conclure à une expiration.

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 ;
  • iat trè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 ;
  • nbf futur ;
  • iss incorrect ;
  • aud incorrect ;
  • 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 expired par route ;
  • nombre de not_active ;
  • distribution de exp - now au 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

ObservationInterprétationÉtape suivante
now >= expexpiration probablerefresh/reauthentication
écart de quelques secondesskew possibleclocks/leeway
nbf > nowpas encore actifissuance/clock
iat futurclock/unitéinfrastructure
exp absentpolicy de profilspécification
exp futur + 401autre validationsignature/iss/aud

Mon workflow exp

  1. Je décode avec JWT Decoder.
  2. Je confirme que exp est numérique et en secondes.
  3. Je convertis en UTC.
  4. Je compare à l’horloge du verifier réel.
  5. Je vérifie iat et nbf.
  6. Je contrôle une clock tolerance explicite.
  7. Je vérifie si le profil exige exp.
  8. Je reproduis le cas avec un test proche de la frontière temporelle.
  9. En cas d’expiration, j’utilise le mécanisme de renouvellement prévu.
  10. 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.

Sources et références

Guides associés