Mensaje de error
Signature has expired
PyJWT (Python) — jwt.exceptions.ExpiredSignatureError
Qué significa
El claim (declaración) exp del token es anterior a la hora actual del verificador, así que PyJWT lo rechazó después de una comprobación de la firma (conceptualmente) correcta. El mensaje induce un poco a error: la firma en sí no caduca; lo que se ha cerrado es la ventana de validez del token.
Por qué ocurre
Un token antiguo reutilizado más allá de su vida útil
frecuenteCredenciales en caché en un worker de larga duración, un token guardado del lado del cliente y reenviado después de su TTL, o pruebas de integración que usan un token de fixture generado hace días. El rechazo es el comportamiento correcto.
Deriva del reloj en el host que verifica
frecuenteUn verificador cuyo reloj va adelantado acorta la vida útil efectiva de todos los tokens. Los contenedores y los runners de CI sin sincronización horaria son los sospechosos habituales; incluso una deriva de entre 60 y 120 segundos rompe de forma intermitente los tokens de vida corta.
El emisor escribe exp de forma incorrecta
ocasionalexp debe ser un número entero de segundos desde la época Unix. Pasar objetos datetime mal serializados, o milisegundos, produce tokens que ya están caducados en el momento de emitirse: entonces cada verificación falla de inmediato, y eso distingue este caso de una caducidad normal.
Se usó un datetime sin zona horaria para generar exp
ocasionalLos emisores que construyen exp a partir de datetime.now() (hora local sin zona horaria) en lugar de datetime.now(timezone.utc) desplazan la vida útil de cada token tanto como el desfase UTC del servidor. Según en qué lado de UTC esté el emisor, los tokens caducan horas antes de tiempo o sobreviven en silencio a su TTL previsto. PyJWT codifica correctamente los datetime con zona horaria; los que no la tienen son la trampa.
Cómo solucionarlo
- 1.
Absorber un desfase pequeño con leeway
El parámetro leeway (en segundos) se aplica a las comprobaciones de exp y nbf. Usa como mucho unas decenas de segundos; los valores mayores alargan en silencio la vida útil de los tokens.
python import jwt payload = jwt.decode( token, key, algorithms=["ES256"], leeway=30, # seconds of tolerated clock skew ) - 2.
Inspeccionar las marcas de tiempo reales
Lee los claims sin verificación y compara exp con el reloj del host: el tamaño de la diferencia te dice si se trata de una caducidad, de un desfase o de un error en la emisión.
python import jwt, time claims = jwt.decode(token, options={"verify_signature": False}) print("exp:", claims["exp"], "now:", int(time.time()), "delta:", int(time.time()) - claims["exp"], "s")