Mensaje de error
token has invalid claims: token is expired
También se muestra como
- Token is expired
- token has invalid claims: token is not valid yet
- token has invalid claims: token used before issued
golang-jwt v4 imprime «Token is expired» (con la T mayúscula). Los demás errores de claims de la v5 usan el mismo prefijo «token has invalid claims:».
golang-jwt/jwt (Go) — jwt.ErrTokenInvalidClaims joined with jwt.ErrTokenExpired
Qué significa
La firma del token se verificó, pero su claim (declaración) `exp` está en el pasado (más allá de cualquier margen de tolerancia que hayas configurado). golang-jwt v5 lo notifica como `ErrTokenInvalidClaims` unido a `ErrTokenExpired`; la misma familia produce «token is not valid yet» para un `nbf` futuro y «token used before issued» para un `iat` futuro. El token analizado se devuelve igualmente junto con el error, pero no se debe confiar en él.
Por qué ocurre
El token sobrepasa su vida útil
frecuenteLos tokens de acceso suelen durar de 5 a 60 minutos. Los clientes que guardan un token en caché, reintentan una solicitud mucho después o lo conservan en un trabajo de larga duración lo presentan después de `exp`.
El reloj del verificador va adelantado
frecuenteUn servidor cuyo reloj adelanta hace caducar antes de tiempo todos los tokens. Los contenedores heredan el reloj del host, así que un solo host sin NTP afecta a todos sus servicios.
El cliente no tiene forma de renovar el token
ocasionalLas aplicaciones de una sola página y las CLI que guardan un token de acceso pero nunca usan el token de actualización (o nunca gestionan el 401) fallan de forma permanente en cuanto caduca el primer token.
exp en la unidad equivocada
rara`exp` son segundos desde la época Unix. Un emisor que divide dos veces los milisegundos o usa una conversión a hora local puede emitir tokens que ya están caducados.
Cómo solucionarlo
- 1.
Detectar la caducidad y dejar que el cliente renueve el token
Asigna a `ErrTokenExpired` una respuesta 401 con la indicación `invalid_token`. El cliente usa entonces su token de actualización (o vuelve a autenticarse) y reintenta.
go token, err := jwt.Parse(raw, keyfunc, jwt.WithValidMethods([]string{"ES256"})) if errors.Is(err, jwt.ErrTokenExpired) { w.Header().Set("WWW-Authenticate", `Bearer error="invalid_token", error_description="token expired"`) http.Error(w, "token expired", http.StatusUnauthorized) // client refreshes and retries return } - 2.
Tolerar un pequeño desfase de reloj y exigir exp
Un margen de tolerancia de unos segundos absorbe la deriva entre máquinas. `WithExpirationRequired` cierra el hueco opuesto: de lo contrario, los tokens sin `exp` no caducarían nunca.
go parser := jwt.NewParser( jwt.WithValidMethods([]string{"ES256"}), jwt.WithLeeway(30*time.Second), // absorbs clock skew for exp, nbf and iat jwt.WithExpirationRequired(), // tokens without exp are rejected too )