Mensagem de erro
Signature has expired
PyJWT (Python) — jwt.exceptions.ExpiredSignatureError
O que significa
A claim exp do token é anterior à hora atual do verificador, por isso o PyJWT o rejeitou depois de uma verificação de assinatura (conceitualmente) bem-sucedida. A mensagem engana um pouco — a assinatura em si não expira; o que se encerrou foi a janela de validade do token.
Por que acontece
Token antigo reutilizado além do tempo de vida
comumCredenciais em cache em um worker de longa duração, um token armazenado no lado do cliente e reenviado depois do TTL ou testes de integração que usam um token de fixture gerado dias antes. A rejeição é o comportamento correto.
Desvio de relógio no host que faz a verificação
comumUm verificador com o relógio adiantado encurta o tempo de vida efetivo de todos os tokens. Contêineres e runners de CI sem sincronização de horário são os suspeitos de sempre; mesmo um desvio de 60 a 120 segundos quebra tokens de vida curta de forma intermitente.
O emissor grava o exp de forma incorreta
ocasionalO exp precisa ser um número inteiro de segundos desde a época Unix (epoch). Passar objetos datetime serializados de forma incorreta, ou milissegundos, gera tokens que já estão expirados no momento da emissão — então toda verificação falha imediatamente, o que distingue este caso de uma expiração comum.
Datetime sem fuso horário (naive) usado para gerar o exp
ocasionalEmissores que montam o exp a partir de datetime.now() (hora local ingênua, sem fuso horário) em vez de datetime.now(timezone.utc) deslocam o tempo de vida de todos os tokens na diferença entre o fuso do servidor e o UTC. Dependendo de que lado do UTC o emissor está, os tokens expiram horas antes ou sobrevivem silenciosamente ao TTL previsto. O PyJWT codifica corretamente datetimes com fuso horário — os ingênuos é que são a armadilha.
Como corrigir
- 1.
Absorver pequenos desvios de relógio com leeway
O parâmetro leeway (em segundos) vale para as verificações de exp e nbf. Use no máximo algumas dezenas de segundos; valores maiores estendem silenciosamente o tempo de vida dos tokens.
python import jwt payload = jwt.decode( token, key, algorithms=["ES256"], leeway=30, # seconds of tolerated clock skew ) - 2.
Inspecionar os timestamps reais
Leia as claims sem verificação e compare o exp com o relógio do host — o tamanho da diferença mostra se é expiração, desvio de relógio ou um bug na emissão.
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")