JWT, ou JSON Web Token, é um formato compacto para transportar claims assinadas entre sistemas. Em aplicações Node.js, ele é usado em autenticação, autorização e comunicação entre serviços. O token não é uma sessão mágica: ele precisa de validação rigorosa, expiração curta, algoritmo fixo, chaves protegidas e estratégia de revogação.
Estrutura
Um JWT possui header, payload e assinatura. Header e payload são codificados, não criptografados. Nunca coloque senha, segredo, documento pessoal ou dado sensível no payload.
import { SignJWT } from 'jose';
const token = await new SignJWT({
sub: String(user.id),
role: user.role,
})
.setProtectedHeader({ alg: 'RS256', kid: 'key-2026-01' })
.setIssuer('https://auth.example.com')
.setAudience('api.example.com')
.setIssuedAt()
.setExpirationTime('10m')
.setJti(crypto.randomUUID())
.sign(privateKey);Claims essenciais
- sub: identidade principal;
- iss: emissor esperado;
- aud: público destinatário;
- exp: expiração;
- iat: emissão;
- nbf: início de validade;
- jti: identificador único.
Valide todas as claims relevantes. Verificar apenas a assinatura permite usar token emitido para outro serviço ou ambiente.
Validação
import { jwtVerify } from 'jose';
const { payload, protectedHeader } = await jwtVerify(token, publicKey, {
algorithms: ['RS256'],
issuer: 'https://auth.example.com',
audience: 'api.example.com',
clockTolerance: 5,
});Fixe explicitamente os algoritmos aceitos. Não aceite o algoritmo indicado pelo token sem uma lista permitida.
Chave simétrica ou assimétrica
HS256 usa o mesmo segredo para assinar e validar. É simples, mas todo verificador pode emitir tokens. RS256 e ES256 permitem distribuir chave pública sem expor a chave privada. Em ambientes com vários serviços, assinatura assimétrica costuma reduzir risco.
kid e rotação
O header kid identifica a chave. Mantenha chaves antigas durante a validade máxima dos tokens e remova depois. Nunca use o kid diretamente como caminho de arquivo ou consulta sem validação.
JWKS
Um endpoint JWKS publica chaves públicas:
import { createRemoteJWKSet, jwtVerify } from 'jose';
const jwks = createRemoteJWKSet(
new URL('https://auth.example.com/.well-known/jwks.json'),
);
await jwtVerify(token, jwks, {
issuer: 'https://auth.example.com',
audience: 'api.example.com',
});Use HTTPS, cache, timeout e limites. O endereço do JWKS deve vir de configuração confiável, nunca do token.
Access token curto
Tokens de acesso devem expirar em minutos. Validade longa aumenta a janela após roubo. Para sessões duradouras, use refresh token rotacionado e revogável.
Armazenamento no navegador
LocalStorage é acessível a JavaScript e pode ser roubado por XSS. Cookie HttpOnly reduz leitura por script, mas reintroduz CSRF. Uma arquitetura comum mantém access token em memória e refresh token em cookie seguro.
Bearer token
Authorization: Bearer eyJ...Bearer significa que quem possui o token pode usá-lo. Proteja logs, traces, analytics, URLs e mensagens de erro. Nunca envie token na query string.
Middleware
async function requireJwt(req, res, next) {
const authorization = req.get('authorization') || '';
const match = authorization.match(/^Bearer (.+)$/);
if (!match) {
res.status(401).json({ error: 'missing_token' });
return;
}
try {
const { payload } = await verifyAccessToken(match[1]);
req.auth = {
userId: payload.sub,
role: payload.role,
jti: payload.jti,
};
next();
} catch {
res.status(401).json({ error: 'invalid_token' });
}
}Não devolva detalhes criptográficos ao cliente.
Autorização
JWT autentica claims, mas o servidor ainda precisa aplicar regras. Roles desatualizadas podem permanecer válidas até a expiração. Para ações críticas, consulte estado atual ou use tokens muito curtos.
Revogação
Opções:
- access token curto;
- lista de jti revogados;
- versão de sessão por usuário;
- revogação de refresh token;
- rotação de chave em incidente.
Uma lista global para todos os tokens elimina parte do benefício stateless e precisa de TTL.
Logout
Logout deve revogar refresh token e limpar cookies. O access token pode permanecer válido por poucos minutos; para risco elevado, registre o jti até expirar.
Clock skew
Pequenas diferenças de relógio podem causar rejeições. Use tolerância mínima e sincronização de tempo. Não configure minutos de tolerância para esconder servidores incorretos.
Token confusion
Não use ID token como access token. Valide issuer, audience, tipo e finalidade. Tokens de ambientes diferentes também devem ter emissores ou públicos distintos.
Claims customizadas
Use nomes estáveis, pequenos e documentados. Não coloque listas enormes de permissões, porque o token cresce e pode ficar desatualizado.
Erros comuns
- não validar audience e issuer;
- aceitar qualquer algoritmo;
- usar segredo curto;
- colocar dados sensíveis no payload;
- token com validade de dias;
- registrar Authorization;
- usar token em URL;
- não planejar rotação;
- usar JWT onde sessão opaca seria mais simples.
Testes
Teste assinatura inválida, token expirado, audience incorreta, issuer incorreto, algoritmo não permitido, kid desconhecido, token revogado e relógio próximo ao limite.
Fluxo recomendado
- defina issuer e audience;
- use biblioteca madura;
- prefira chave assimétrica em múltiplos serviços;
- fixe algoritmos;
- mantenha access token curto;
- proteja refresh token;
- implemente rotação;
- redija logs;
- teste revogação.
Combine JWT com Cookies Seguros, Secret Management, Rate Limiting e Pino.
Consulte a especificação JWT e a documentação da biblioteca jose.




