Cuando veo token expired, jwt expired o un 401 que parece temporal, compruebo exp temprano pero no lo analizo aislado. Convierto el valor, lo comparo con el reloj del sistema que valida y miro también iat y nbf. Así separo expiración real de unidades equivocadas, clock skew y políticas adicionales.
Para una lectura rápida uso JWT Expiry Checker.
exp es NumericDate en segundos
RFC 7519 define exp como Expiration Time:
{
"iat": 1789200000,
"nbf": 1789200000,
"exp": 1789203600
}
Conceptualmente:
current verifier time < exp
Cuando el reloj del verifier alcanza o supera exp, un perfil que aplica expiración debe rechazarlo, salvo una pequeña tolerancia explícita.
El error clásico: segundos contra milisegundos
const now = Math.floor(Date.now() / 1000);
const expired = typeof payload.exp === "number" && now >= payload.exp;
También puede hacerse:
const expired = Date.now() >= payload.exp * 1000;
Lo que no hago es comparar Date.now() directamente con payload.exp.
Cómo lo compruebo en Python
import time
if isinstance(payload.get("exp"), (int, float)) and time.time() >= payload["exp"]:
raise ValueError("token expired")
Estos snippets explican tiempo, no sustituyen una librería de verificación de firma y claims.
Clock skew: tolerancia pequeña, no extensión arbitraria
RFC 7519 permite una pequeña leeway por diferencias de reloj. La uso solo cerca del límite:
exp 12:00:00Z
verifier clock 12:00:03Z
leeway 5s
Si exp quedó 45 minutos atrás, clock skew no es una explicación razonable.
Leo iat, nbf y exp como una línea temporal
issued -------- active window -------- expired
iat nbf exp
Señales sospechosas:
nbf > exp;iatmuy en el futuro;exp < iat;- lifetime inesperadamente largo;
- todos los valores desplazados por factor 1000.
JWT Validator me ayuda a ver estas reglas junto a firma e issuer/audience.
Missing exp depende del perfil
RFC 7519 define el claim, pero cada perfil decide qué es obligatorio. RFC 7523, por ejemplo, sí impone exp en su contexto.
Antes de marcarlo como error pregunto:
- qué tipo de token es;
- qué perfil aplica;
- si la API exige expiración;
- si hay maximum token age adicional.
Un token puede estar dentro de exp y aun así fallar
Otras causas:
- firma inválida;
nbffuturo;issequivocado;audequivocado;- token type incorrecto;
- scopes/roles insuficientes;
- revocation/session policy.
Si el tiempo está bien, uso JWT Debugger en lugar de seguir culpando a exp.
Editar exp no renueva un JWT firmado
Cambiar:
{ "exp": 1789203600 }
por otro timestamp cambia el payload firmado. La firma anterior debe dejar de ser válida. El renewal correcto emite un token nuevo.
Matriz de diagnóstico
| Observación | Interpretación | Siguiente paso |
|---|---|---|
now >= exp | expiración probable | refresh/reauthentication |
| diferencia de pocos segundos | posible skew | revisar clocks/leeway |
nbf > now | aún no activo | revisar emisión/reloj |
iat en futuro | clock/unidad | revisar infraestructura |
exp ausente | policy del perfil | consultar especificación |
exp futuro + 401 | otra validación | firma/iss/aud |
Mi flujo de comprobación
- Decodifico el JWT con JWT Decoder.
- Confirmo que
expes numérico y está en segundos. - Convierto a UTC.
- Comparo con el reloj del verifier real.
- Reviso
iatynbf. - Compruebo clock tolerance explícita.
- Confirmo si el perfil exige
exp. - Si está expirado, uso el mecanismo legítimo de renewal.
- Si sigue activo, paso a firma, issuer y audience.
exp responde si el token está fuera de su ventana temporal. No responde por sí solo si una aplicación debe confiar en él.
Registro el contexto de cada rechazo
Cuando un verifier rechaza un token, conservo el valor de exp, la hora UTC observada, la tolerancia aplicada y el componente que tomó la decisión. También comparo el mismo token contra un entorno conocido, sin copiarlo en logs públicos. Este contexto permite distinguir un error de unidad, un reloj desfasado o una política de duración máxima de una expiración real y reproducible.
Antes de modificar la tolerancia, repito la comparación con una hora de referencia independiente y confirmo que todos los servicios usan segundos Unix. Si un gateway y una API toman decisiones distintas, reviso sus configuraciones por separado: compartir el mismo JWT no garantiza que compartan reloj, profile ni política de validación.