Mensaje de error
"exp" claim timestamp check failed
jose (JavaScript) — JWTExpired, code ERR_JWT_EXPIRED
Qué significa
La validación de claims (declaraciones) de jose rechazó el token porque su marca de tiempo exp está en el pasado según el reloj del verificador. La comprobación de la firma ya se había superado: se trata solo de un fallo de vigencia. jose usa el mismo patrón de texto para otros claims («"nbf" claim timestamp check failed») cuando un token se usa antes de su momento de inicio de validez.
Por qué ocurre
El token se usa más allá de su vida útil
frecuenteLos tokens de acceso están pensados para durar poco. Un cliente que guarda tokens en caché sin comprobar su caducidad, un consumidor de cola que procesa un mensaje con un token dentro horas después de que se encolara, o unos datos de prueba con un token fijo en el código acabarán aquí siempre, de forma determinista.
El reloj del verificador va por delante del reloj del emisor
frecuenteUnos minutos de desfase del reloj en el host que verifica acortan la vida útil efectiva de cada token y pueden provocar el rechazo de tokens recién emitidos. Los contenedores heredan el reloj de su host: una VM cuyo reloj se desvía rompe a la vez todos los pods que ejecuta.
Unidad incorrecta en exp
ocasionalexp debe expresarse en segundos desde la época Unix (RFC 7519). El código emisor que divide mal o pasa un objeto Date serializado en milisegundos produce tokens que nacen ya caducados o que valen durante milenios.
Cómo solucionarlo
- 1.
Añadir una tolerancia de reloj acotada
jose acepta una opción clockTolerance (un número de segundos o una cadena legible por personas). Mantenla baja: sirve para absorber el desfase, no para alargar la vida útil.
js import { jwtVerify } from "jose"; await jwtVerify(token, key, { algorithms: ["ES256"], clockTolerance: "30s", }); - 2.
Imprimir la cronología antes de cambiar nada
Compara iat, exp y la hora actual del verificador. El patrón te indica la causa: exp ya en el pasado → problema de renovación del token; exp anterior a iat → error al emitirlo; exp ≈ ahora, pero falla → desfase de reloj.
js import { decodeJwt } from "jose"; const { iat, exp } = decodeJwt(token); console.log({ iat: new Date(iat * 1000), exp: new Date(exp * 1000), now: new Date() });