Cuando una API dice token expired, jwt expired o devuelve invalid_token, primero confirmo si el JWT está realmente fuera de su ventana temporal. Distingo expiración real de clock skew, una política adicional del servidor o un 401 genérico que en realidad viene de firma, issuer o audience.
Empiezo con JWT Expiry Checker para leer exp, iat y nbf en UTC.
Paso 1: interpreto exp en segundos
JWT NumericDate usa segundos desde Unix epoch:
{
"iat": 1789200000,
"exp": 1789203600
}
En JavaScript:
const expiresAtMs = payload.exp * 1000;
const expired = Date.now() >= expiresAtMs;
Comparar directamente Date.now() con un exp de diez dígitos mezcla milisegundos y segundos.
Paso 2: comparo contra el reloj del verifier
La hora que importa es la del sistema que rechaza el token: API, gateway o middleware.
exp 12:00:00Z
API clock 12:00:06Z
clock leeway 5s
=> expired
Pocos segundos alrededor del límite pueden ser skew. Cuarenta minutos no.
Paso 3: reviso nbf, iat y reglas de edad máxima
Un token puede fallar aunque exp esté en el futuro porque:
nbftodavía no se alcanzó;iataparece anormalmente en el futuro;- la aplicación limita la edad máxima del token;
- existe revocation/session policy adicional.
Por eso uso JWT Validator cuando necesito una decisión completa y no solo tiempo.
Paso 4: leo el error del servidor con contexto
RFC 6750 usa invalid_token para varios tipos de token inválido, no solo expiración. Busco mensajes como:
401 Unauthorized
invalid_token
TokenExpiredError
token expired
signature has expired
token not active
Si logs y timestamps coinciden, acepto la hipótesis de expiración. Si no, cambio de hipótesis.
Paso 5: elijo refresh o reauthentication
No edito exp. Cambiar el payload rompe la firma existente.
| Situación | Acción |
|---|---|
| Access Token expirado + refresh válido | obtener Access Token nuevo |
| refresh expirado/revocado | reauthentication |
| Client Credentials | pedir un token nuevo al authorization server |
| no existe refresh flow | ejecutar de nuevo el flujo autorizado |
exp sigue en futuro | investigar otra validación |
Race conditions durante refresh
Cuando varias requests detectan el token expirando al mismo tiempo, todas pueden intentar refresh. Si el sistema rota refresh tokens, una segunda request puede reutilizar un token ya consumido y provocar otra cadena de 401.
En clientes reales coordino el refresh, actualizo credenciales de forma atómica y evito que requests paralelas sigan usando el access token viejo.
Un token expirado puede tener firma válida
No hay contradicción:
signature valid ✓
issuer valid ✓
audience valid ✓
expiration valid ✕
La firma y la expiración responden preguntas distintas. Para aislar la primera uso JWT Signature Verifier.
Si falta exp
No todos los perfiles JWT hacen obligatorio exp. Reviso la especificación concreta y la política de la aplicación.
Para access tokens de vida corta, missing exp es una señal de policy que merece atención, pero no lo etiqueto automáticamente como JWT sintácticamente roto.
Qué reviso cuando parece expirar demasiado pronto
- sincronización de reloj de API/gateway;
- reloj del identity provider;
- seconds vs milliseconds;
- clock tolerance configurada;
- maximum token age;
- environment/token cache equivocado;
- revocation/session policy.
Una tolerancia grande no sustituye relojes correctos.
Mantengo separado refresh y validación
Un refresh token, una cookie de sesión o una nueva autenticación pueden recuperar una sesión, pero no cambian el significado del access token que ya expiró. Registro qué componente decidió rechazarlo y qué tiempos comparó, porque así evito confundir un problema de reloj, cache o revocación con una firma inválida.
Mi flujo de recuperación
- Abro JWT Expiry Checker.
- Confirmo
exp,iatynbfcomo NumericDate en segundos. - Comparo
expcon el clock del verifier real. - Leo
WWW-Authenticatey logs si están disponibles. - Distingo expiración de otras causas de
invalid_token. - Si expiró, uso el flujo legítimo de refresh o reauthentication.
- No edito
expni reutilizo una firma antigua. - Si el token sigue activo, reviso firma,
issyaudcon JWT Debugger.
La idea clave: expired no significa corrupto. Un JWT puede estar bien formado y correctamente firmado, pero ya fuera de su ventana de aceptación.