A maioria dos bugs de JWT em TypeScript não está no token — está na configuração de verificação. Uma restrição de algoritmo ausente, um claim de emissor sem verificação ou um segredo de vida curta podem minar silenciosamente a autenticação de uma forma que só aparece em produção. Este guia percorre todas as dimensões da correção da configuração JWT em TypeScript: algoritmos, validação de claims, higiene de segredos, tratamento de expiração e as ferramentas de navegador que agilizam a depuração.
O que a validação de configuração JWT cobre
Um JSON Web Token (JWT) é uma string compacta e segura para URL formada por três partes codificadas em Base64URL separadas por pontos: um cabeçalho que declara o algoritmo e o tipo de token, um payload que carrega os claims e uma assinatura que os vincula. Validar um JWT significa confirmar que as três partes estão intactas e que os claims atendem aos requisitos da sua aplicação — não apenas que a assinatura é matematicamente correta.
As duas camadas da validação JWT
A validação criptográfica confirma a assinatura: o servidor verifica que o token foi assinado com a chave esperada e não foi adulterado. A validação de configuração vai além: verifica que o token foi emitido pela autoridade certa, destina-se a este serviço específico, não expirou e carrega os claims personalizados esperados. A maioria das vulnerabilidades de segurança JWT vem de validação de configuração incompleta, não de criptografia quebrada.
- Algoritmo (`alg`) - deve corresponder exatamente à configuração do seu servidor; nunca o infira do cabeçalho do token
- Expiração (`exp`) - o token não deve ter passado do carimbo de data/hora de expiração, considerando a tolerância de relógio
- Não-antes-de (`nbf`) - o token não deve ser usado antes do seu primeiro horário válido
- Emissor (`iss`) - o token deve se originar do seu serviço de autenticação confiável
- Audiência (`aud`) - o token deve se destinar a esta API ou serviço específico
- Claims personalizados - papel, escopo, ID de tenant ou qualquer campo específico da aplicação do qual sua lógica dependa
Note
Checagens de algoritmo e configuração de chaves
A configuração do algoritmo é a definição mais crítica de segurança na verificação JWT. Errar nisso habilita uma classe de ataques que contorna completamente a autenticação. As bibliotecas JWT de TypeScript dão as ferramentas para aplicá-lo corretamente - mas só se você as usar explicitamente.
HS256 vs RS256 - escolhendo o algoritmo certo
| Propriedade | HS256 (simétrico) | RS256 (assimétrico) |
|---|---|---|
| Tipo de chave | Segredo compartilhado (mesma chave para assinar + verificar) | Par de chaves RSA (privada para assinar, pública para verificar) |
| Distribuição de chaves | Todo verificador detém o segredo | Apenas o emissor detém a chave privada |
| Segurança multi-serviço | ✗ Arriscado - todos os verificadores podem forjar tokens | ✓ Verificadores detêm apenas a chave pública |
| Suporte a OIDC / JWKS | ✗ Não aplicável | ✓ Chaves públicas servidas via endpoint JWKS |
| Desempenho | ✓ Rápido (HMAC) | ✗ Mais lento (matemática RSA) |
| Melhor para | Ferramentas internas, APIs de serviço único | APIs de produção, sistemas distribuídos, OIDC |
O ataque de confusão de algoritmo - e como preveni-lo
A confusão de algoritmo acontece quando um servidor lê o campo `alg` do cabeçalho JWT para decidir como verificar o token, em vez de forçar o algoritmo a partir da sua própria configuração. Um atacante modifica o cabeçalho para trocar `RS256` por `HS256`, e então assina o token com a chave pública do servidor usada como segredo HMAC. Um servidor mal configurado o aceita como válido. A correção é uma única linha de código - mas precisa estar presente.
// ✗ Vulnerable - algorithm inferred from token header
jwt.verify(token, publicKey);
// ✓ Correct - algorithm enforced from server configuration
jwt.verify(token, publicKey, { algorithms: ['RS256'] });Warning
Validação da força da chave para HS256
Ao usar HS256, o segredo deve ter pelo menos 256 bits (32 bytes) para corresponder ao tamanho de saída do SHA-256. Segredos curtos - menos de 32 caracteres, palavras de dicionário ou strings estáticas como `"secret"` ou `"development"` - são quebrados trivialmente por força bruta com ferramentas como `hashcat` ou `jwt_tool`. Gere segredos com uma fonte aleatória criptograficamente segura: `crypto.randomBytes(32).toString('hex')` no Node.js produz uma string hexadecimal de 64 caracteres que atende ao requisito mínimo de entropia.
Validar claims JWT padrão em TypeScript
A especificação JWT define um conjunto de claims registrados padrão que toda implementação deveria entender. A biblioteca `jsonwebtoken` valida vários deles automaticamente quando você passa as opções certas - mas a palavra-chave é "quando você as passa". Sem configuração explícita, a maioria das checagens de claims é silenciosamente pulada.
Os claims exp, nbf e iat
O claim exp (expiração) é um carimbo de data/hora Unix após o qual o token deixa de ser válido. A biblioteca jsonwebtoken verifica exp por padrão durante jwt.verify(). Porém, o desvio de relógio entre o emissor do token e o verificador pode fazer tokens válidos serem rejeitados - fonte comum de erros de "token expirado" em sistemas distribuídos onde os relógios dos servidores derivam por segundos. Passe clockTolerance para permitir uma janela pequena: definir clockTolerance como 30 aceita tokens até 30 segundos após o valor exp.
interface JwtPayload {
sub: string;
iss: string;
aud: string;
exp: number;
iat: number;
role: 'admin' | 'user';
}
const payload = jwt.verify(token, publicKey, {
algorithms: ['RS256'],
issuer: 'https://auth.example.com',
audience: 'api.example.com',
clockTolerance: 30, // seconds of clock skew to tolerate
}) as JwtPayload;Os claims iss e aud
O claim `iss` (emissor) identifica a origem do token. O claim `aud` (audiência) identifica o destinatário pretendido. Ambos são opcionais na especificação JWT, mas críticos na prática. Sem validação de `iss`, qualquer serviço que possa produzir tokens válidos com sua chave de assinatura pode autenticar-se na sua API. Sem validação de `aud`, um token emitido para seu app móvel pode ser reutilizado contra sua API administrativa. Passe ambos como opções no `jwt.verify()` para que a biblioteca os imponha como requisitos rígidos em vez de campos informativos.
Tip
Implementar validação JWT em TypeScript
Uma função completa de verificação JWT em TypeScript lida com validação criptográfica, validação de claims e classificação de erros em um só lugar. Veja como estruturá-la - com os quatro passos que correspondem ao esquema HowTo.
Verifique que o algoritmo corresponde ao seu tipo de chave
Antes de escrever qualquer código de verificação, confirme sua combinação de algoritmo e chave. RS256 exige uma chave privada RSA para assinar e a chave pública correspondente para verificar. HS256 exige o mesmo segredo compartilhado dos dois lados. Confundi-los causa exceções em tempo de execução difíceis de diagnosticar. Guarde sua chave pública ou segredo compartilhado em variáveis de ambiente - nunca os escreva em arquivos-fonte.
Valide explicitamente os claims exp e nbf
Sempre defina `clockTolerance` para lidar com pequenas derivações de relógio entre serviços. Um valor de 30 segundos é um padrão razoável para a maioria dos sistemas distribuídos. Ao depurar `TokenExpiredError` em produção, registre `payload.exp * 1000` e `Date.now()` juntos - isso mostra exatamente quantos milissegundos o token passou da expiração, distinguindo expiração real de um problema de sincronização de relógios entre seu serviço de autenticação e o servidor da API.
Verifique os claims iss e aud contra os valores esperados
Passe as opções `issuer` e `audience` ao `jwt.verify()` para que a biblioteca rejeite tokens com valores divergentes antes que seu código de aplicação rode. Se seu sistema tem múltiplas audiências válidas (por exemplo, tanto `api.example.com` quanto `admin.example.com`), passe um array: `audience: ['api.example.com', 'admin.example.com']`. A biblioteca aceita o token se o claim `aud` corresponder a qualquer entrada do array.
Teste sua configuração com o Decodificador JWT
Antes de rodar sua suíte de testes TypeScript, cole um token de exemplo do seu ambiente de desenvolvimento ou staging no Decodificador e Validador JWT. Confirme visualmente que cada claim - `alg`, `exp`, `iss`, `aud` e seus claims personalizados - corresponde às opções do seu `jwt.verify()`. Essa checagem de um minuto captura divergências entre o que o token contém e o que seu código espera, antes de gastar tempo depurando em um ambiente de teste.
Decodificador e Validador JWT
Decodifique tokens JWT e inspecione todos os claims, campos do cabeçalho e problemas de segurança comuns instantaneamente no seu navegador - sem cadastro, sem envio ao servidor.
Erros comuns de configuração JWT
Estes são os erros de configuração que aparecem com mais frequência em implementações JWT em TypeScript. Cada um é silencioso na inicialização e só se manifesta como uma falha de autenticação ou incidente de segurança em produção.
Usar `jwt.decode()` em vez de `jwt.verify()`
A função `jwt.decode()` extrai o payload sem verificar a assinatura. É útil para inspecionar um token em que você já confia - como extrair um ID de usuário de um token já validado pelo middleware. Não é um substituto do `jwt.verify()`. Código que usa `jwt.decode()` para obter claims e depois toma decisões de autorização com base nesses claims está aceitando tokens não verificados. Isso é um bypass completo da autenticação.
Ignorar o tipo de erro nos blocos catch
A biblioteca `jsonwebtoken` lança três tipos de erro distintos: `JsonWebTokenError` (token malformado ou assinatura inválida), `TokenExpiredError` (além do claim `exp`) e `NotBeforeError` (antes do claim `nbf`). Capturar todos os erros como um `Error` genérico e retornar `401 Unauthorized` para todos os casos perde informação de diagnóstico. Trate cada tipo separadamente e retorne mensagens específicas - `token expirado` versus `token inválido` - para que clientes e sistemas de monitoramento distingam problemas de configuração de tentativas de ataque reais.
Não rotacionar segredos ou pares de chaves
Segredos de assinatura de vida longa acumulam risco com o tempo. Um segredo que nunca foi rotacionado significa que todo token já emitido com ele permanece válido se o segredo for comprometido. Implemente um campo de ID de chave (`kid`) no seu cabeçalho JWT para que o verificador possa buscar a chave pública correta em um endpoint JWKS. Esse padrão permite rotação de chaves sem invalidar tokens assinados pela chave anterior - cada token carrega uma referência à chave específica que o assinou. O Inspetor de JWK ajuda a validar a saída do endpoint JWKS e detectar material de chave privada que não deveria estar exposto publicamente.
Warning
Aceitar o algoritmo `none`
O algoritmo `none` produz um JWT não assinado - qualquer payload com estrutura válida passa na verificação. Versões antigas das bibliotecas JWT aceitavam `none` por padrão. Bibliotecas modernas o rejeitam, mas apenas quando você especifica explicitamente os algoritmos permitidos nas opções de verificação. Inclua sempre `algorithms: ['RS256']` (ou seu algoritmo específico) para tornar a rejeição de `none` explícita e resiliente a mudanças de versão da biblioteca.
Depurar problemas de JWT com ferramentas no navegador
Escrever um teste para reproduzir um erro de JWT costuma ser mais lento do que inspecionar o token diretamente. Ferramentas de navegador permitem examinar os claims, o estado de expiração e a estrutura de chaves de um token em segundos - sem ambiente local, sem executar código e sem enviar dados sensíveis a um serviço de terceiros.
Decodificar e inspecionar claims
O Decodificador e Validador JWT decodifica o cabeçalho e o payload de qualquer string JWT e apresenta todos os claims em formato estruturado e legível. Ele verifica problemas de segurança comuns - `exp` ausente, algoritmo fraco, `aud` ausente - e os sinaliza com diagnósticos claros. Cole qualquer token do seu ambiente de desenvolvimento, staging ou produção e confirme em dez segundos se os claims correspondem ao que sua chamada `jwt.verify()` espera.
Diagnosticar problemas de expiração
A Calculadora de Contagem Regressiva de Expiração JWT lê o claim `exp` de qualquer token e mostra o tempo de vida restante exato ou o tempo desde a expiração tanto em UTC quanto no horário local. Quando um usuário relata "token expirado" mas seus logs mostram que o token ainda deveria ser válido, cole-o na calculadora. A comparação de carimbos de data/hora em nível de milissegundo revela se o problema é expiração genuína, desvio de relógio entre serviços ou um valor `exp` armazenado em milissegundos em vez de segundos - um bug de fator 1000 surpreendentemente comum.
Inspecionar conjuntos de chaves JWKS
Ao validar tokens de um provedor OIDC ou de qualquer serviço que publique um endpoint JWKS, o Inspetor de JWK analisa e valida a estrutura do conjunto de chaves. Ele verifica que cada chave tem os campos obrigatórios (`kty`, `use`, `alg`, `kid`), valida o tipo de chave e a curva para chaves EC, e sinaliza qualquer material de chave privada que não deveria estar exposto publicamente. Cole o JSON do seu endpoint JWKS diretamente na ferramenta ou informe a URL para um fetch.
Calculadora de Contagem Regressiva de Expiração JWT
Calcule a contagem regressiva exata de expiração JWT a partir do claim exp - veja o tempo restante ou o tempo desde a expiração em UTC e horário local, sem código.
Boas práticas de validação JWT
Uma função de verificação JWT corretamente configurada é necessária, mas não suficiente para autenticação segura. Essas práticas completam o quadro para serviços TypeScript de produção.
Use uma interface de payload tipada
Defina uma interface TypeScript para seu payload JWT e faça cast do resultado da verificação para ela. Isso dá segurança em tempo de compilação quanto aos nomes de claims e tipos de valores - um nome de claim mal escrito (`userId` vs `user_id`) vira um erro de TypeScript em vez de um `undefined` silencioso em tempo de execução. Mantenha a interface em um módulo de tipos compartilhado para que seja consistente em todos os serviços que verificam o mesmo formato de token.
Vidas curtas de token com refresh tokens
Tokens de acesso devem ter vidas curtas - 15 minutos a 1 hora para a maioria das APIs. Tokens de acesso de vida longa (dias, semanas) aumentam a janela durante a qual um token comprometido pode ser usado. Use um fluxo separado de refresh token com expiração longa para persistência de sessão. O refresh token rotaciona a cada uso, e listas de revogação só são necessárias para refresh tokens - não para tokens de acesso - quando as vidas são mantidas curtas.
Centralize a lógica de verificação
Escreva a verificação JWT em um só lugar - uma função de middleware ou uma utilidade compartilhada - e use-a em todos os lugares. Lógica de verificação duplicada convida à deriva de configuração: um endpoint checa `aud`, outro esquece, e a inconsistência é explorada antes que alguém perceba. Middleware do Express, guards do NestJS e handlers de rota do Next.js têm padrões limpos para centralizar checagens de autenticação. Coloque suas opções `algorithms`, `issuer` e `audience` em um único objeto de configuração importado por todo o codebase.
Configure a verificação uma vez, imponha-a em todos os lugares. A única opção JWT que deve diferir entre endpoints é a audiência esperada.
Tip
Key takeaways
- Passe sempre `algorithms: ['RS256']` (ou seu algoritmo específico) explicitamente ao `jwt.verify()` - nunca deixe a biblioteca inferi-lo do cabeçalho do token.
- Valide os claims `iss` e `aud` em toda chamada de verificação; omiti-los permite que tokens de outros serviços autentiquem-se contra sua API.
- Use `clockTolerance` para lidar com desvio de relógio entre serviços distribuídos, e registre carimbos `exp` junto com `Date.now()` ao depurar erros de expiração.
- Nunca use `jwt.decode()` para decisões de autorização - ele pula completamente a verificação de assinatura e aceita qualquer payload de token.
- O Decodificador e Validador JWT permite inspecionar todos os claims e sinalizar problemas de configuração em segundos, sem escrever ou rodar código.
- RS256 é preferível ao HS256 para APIs de produção - elimina o risco de distribuição de segredo compartilhado e suporta rotação de chaves baseada em JWKS.
- Mantenha vidas curtas dos tokens de acesso (15-60 minutos) e centralize toda a lógica de verificação em um único middleware ou utilidade para prevenir deriva de configuração.