Quando a verificação começa a falhar perto de uma troca de chave, trato o caso como problema de ciclo de vida da chave, não apenas como “invalid signature”. O token pode estar correto e o verifier também; a falha pode estar no momento em que a chave antiga sai, a nova entra ou o cache JWKS deixa de acompanhar a mudança.
A pergunta que tento responder é:
Este token foi assinado com uma chave que o verifier ainda consegue encontrar e confiar durante toda a janela em que o token pode ser aceito?
Com token e JWKS em mãos, começo no Validador JWKS.
1. Leio kid, iss, alg e exp
Um cabeçalho pode conter:
{
"alg": "RS256",
"kid": "signing-key-2026-09"
}
E o payload:
{
"iss": "https://login.example.com/",
"exp": 1789203600
}
Quero saber:
- qual issuer é responsável pela chave;
- qual
kido token usa para seleção; - qual algoritmo é esperado;
- por quanto tempo esse token ainda pode precisar ser verificado.
A RFC 7517 descreve kid como mecanismo para escolher entre chaves em um JWK Set, inclusive durante rollover.
2. Comparo o JWKS atual com o kid
Durante uma rotação planejada, posso esperar overlap:
{
"keys": [
{"kid":"signing-key-2026-06","kty":"RSA","alg":"RS256","use":"sig"},
{"kid":"signing-key-2026-09","kty":"RSA","alg":"RS256","use":"sig"}
]
}
Se um token antigo ainda válido usa kid=signing-key-2026-06, remover a chave pública antiga cedo demais pode quebrar sua verificação mesmo com exp no futuro.
O guia de JWT kid ajuda quando o sintoma é apenas kid not found.
3. Separo rotação planejada de revogação de emergência
Não aplico a mesma política em todos os casos.
Em rotação planejada, normalmente quero publicar a nova chave, começar a assinar com ela e manter a chave pública antiga disponível pelo tempo necessário para tokens já emitidos.
Em comprometimento de chave, manter a chave antiga confiável até todos os tokens expirarem pode ser exatamente o que não quero. A política de incidente pode exigir rejeitar imediatamente tokens assinados por uma chave comprometida.
Portanto, não transformo “manter overlap” em regra absoluta. O motivo da troca importa.
4. Verifico cache de JWKS
É comum o issuer publicar a chave nova corretamente e a aplicação continuar usando um conjunto antigo em memória.
Investigo:
- TTL do cache JWKS;
- refresh periódico;
- refresh controlado ao encontrar
kiddesconhecido; - cache em gateway, framework e aplicação;
- instâncias com estados diferentes;
- CDN/proxy servindo metadata antiga;
- fallback que fixa uma chave velha depois de erro de rede.
Um kid desconhecido pode disparar refresh da origem JWKS já confiável, com rate limiting e controles normais de rede.
O que evito é deixar dados arbitrários do token escolherem uma URL nova para baixar chaves.
5. Confirmo que a chave nova pertence ao mesmo issuer confiável
A sequência que quero preservar é:
issuer esperado
↓
metadata/configuração confiável
↓
JWKS confiável
↓
kid compatível
↓
JWK compatível
↓
verificação da assinatura
Se uma implantação pega por engano o JWKS de staging e encontra um kid parecido, não considero isso uma rotação bem-sucedida.
O guia de validação de issuer cobre essa ligação.
6. Confiro metadados da JWK
Mesmo com kid correto, verifico:
{
"kty": "RSA",
"kid": "signing-key-2026-09",
"use": "sig",
"alg": "RS256"
}
kty, alg, use e key_ops, quando presentes, precisam ser compatíveis com a operação de assinatura. A allowlist de algoritmo pertence à aplicação; não vem do token.
7. Comparo o tempo da rotação com a vida do token
Imagine access tokens de 60 minutos:
11:59 token assinado com chave antiga
12:00 chave nova começa a assinar
12:01 chave pública antiga removida
12:30 token antigo ainda tem 29 minutos de exp
Em uma rotação planejada, isso cria uma lacuna. O período de publicação simultânea das chaves precisa refletir o tempo máximo em que tokens antigos continuam aceitáveis, mais os buffers operacionais definidos pelo sistema.
Não deduzo esse período da RFC. Ele depende da política do provedor e da aplicação.
8. Comparo tokens antes e depois da rotação
Quando possível, pego:
- um token emitido pouco antes;
- um token emitido pouco depois.
Comparo kid, issuer, exp e resultado da assinatura contra o JWKS atual.
Isso separa rapidamente:
- chave antiga removida cedo;
- chave nova ainda não publicada;
- cache antigo;
- issuer/ambiente errado;
- falha geral de assinatura sem relação com rotação.
9. Não desativo assinatura durante rollout
Um workaround perigoso seria:
A chave nova ainda não chegou, então vamos ignorar assinatura por alguns minutos.
Não faço isso. Corrijo distribuição de chaves, cache ou ordem do rollout.
Um padrão saudável de rotação planejada é:
1. Publicar nova chave pública no JWKS
2. Manter a chave pública antiga
3. Começar a assinar novos tokens com a nova chave/kid
4. Verificadores aceitam as duas chaves públicas
5. Esperar a janela de tokens antigos terminar
6. Remover a chave antiga quando a política permitir
Meu checklist de rotação JWT
- Decodifico
iss,alg,kideexp. - Confirmo o issuer esperado.
- Inspeciono o JWKS confiável atual.
- Procuro o
kiddo token. - Confiro
kty,alg,useekey_ops. - Verifico a assinatura no Validador JWKS.
- Inspeciono idade e política de refresh do cache.
- Comparo vida de token com disponibilidade da chave antiga.
- Distingo rollover planejado de revogação de emergência.
- Mantenho verificação de assinatura ativa durante toda a correção.
Para mim, rotação de chave JWT é coordenação entre publicação de chave pública, seleção por kid, cache, vida do token e confiança no issuer — não apenas trocar uma chave privada.