O JWKS no Node.js permite distribuir chaves públicas usadas para verificar JWTs assinados. Em vez de copiar certificados ou chaves manualmente para cada API, o emissor publica um JSON Web Key Set em uma URL HTTPS. O token inclui um kid, e a aplicação seleciona a chave correspondente para validar a assinatura.
Esse mecanismo é comum em OpenID Connect, OAuth 2.0, gateways e serviços internos. Ele também facilita rotação de chaves sem interromper consumidores. Porém, uma implementação insegura pode aceitar algoritmos errados, buscar chaves de uma URL controlada pelo token, ignorar issuer e audience ou falhar durante a rotação.
Neste guia, você aprenderá o formato JWK e JWKS, como validar JWTs com a biblioteca jose, configurar cache, tratar rotação, proteger o endpoint, publicar apenas material público e testar falhas.
O que é JWK?
A especificação RFC 7517 define JSON Web Key como um objeto JSON que representa uma chave criptográfica. Um JWK pode ser público, privado ou simétrico, dependendo dos campos presentes.
Uma chave RSA pública:
{
"kty": "RSA",
"use": "sig",
"alg": "RS256",
"kid": "2026-09-primary",
"n": "base64url-modulus",
"e": "AQAB"
}Os campos n e e representam módulo e expoente público.
O que é JWKS?
JWKS é um objeto com uma lista de chaves:
{
"keys": [
{
"kty": "RSA",
"use": "sig",
"alg": "RS256",
"kid": "2026-09-primary",
"n": "...",
"e": "AQAB"
}
]
}O media type registrado é application/jwk-set+json.
Campos importantes
kty: tipo da chave, como RSA, EC ou OKP.kid: identificador usado durante rotação.alg: algoritmo esperado.use: uso, normalmentesigouenc.key_ops: operações permitidas, comoverify.nee: componentes RSA públicos.crv,xey: componentes de curva elíptica.
kid precisa ser único dentro do conjunto durante o período de publicação.
Endpoint comum
Em OpenID Connect, a URL vem do discovery:
https://identity.example.com/.well-known/openid-configurationO documento informa:
{
"issuer": "https://identity.example.com",
"jwks_uri": "https://identity.example.com/.well-known/jwks.json"
}Veja OpenID Connect no Node.js.
Instalando jose
A biblioteca jose implementa JWT, JWS, JWE, JWK e JWKS:
npm install joseValidando com remote JWKS
import {
createRemoteJWKSet,
jwtVerify
} from 'jose';
const issuer = 'https://identity.example.com';
const audience = 'orders-api';
const jwks = createRemoteJWKSet(
new URL(`${issuer}/.well-known/jwks.json`)
);
const { payload, protectedHeader } = await jwtVerify(
accessToken,
jwks,
{
issuer,
audience,
algorithms: ['RS256']
}
);A biblioteca lê kid, encontra a chave, valida a assinatura e verifica claims configuradas.
Não aceite qualquer algoritmo
Defina allowlist:
algorithms: ['RS256']Não escolha o algoritmo apenas com base no header do token. O emissor e a API precisam concordar previamente.
Issuer e audience são obrigatórios
Uma assinatura válida prova que a chave assinou o token. Ela não prova que o token foi criado para sua API.
await jwtVerify(token, jwks, {
issuer: 'https://identity.example.com',
audience: 'orders-api',
algorithms: ['RS256']
});Sem audience, uma API pode aceitar token destinado a outro serviço. Consulte JWT Seguro no Node.js.
URL de JWKS controlada pela aplicação
Nunca use jku ou outra URL do próprio token sem validação:
// Perigoso
const url = new URL(protectedHeader.jku);Um atacante poderia apontar para uma chave própria. Configure issuer e JWKS URI em ambiente controlado ou obtenha por discovery de um issuer permitido.
Proteção contra SSRF
- Use allowlist de issuers.
- Exija HTTPS.
- Não permita endereços privados inesperados.
- Não siga redirects para hosts diferentes.
- Defina timeout e limite de resposta.
- Não use parâmetros do usuário como URL.
Veja SSRF no Node.js.
Cache
Buscar o endpoint em toda requisição seria lento e frágil. Bibliotecas de remote JWKS mantêm cache e evitam novas chamadas enquanto a chave conhecida continua válida.
O cache precisa equilibrar:
- latência;
- disponibilidade;
- tempo para reconhecer nova chave;
- proteção contra excesso de
kiddesconhecido.
Kid desconhecido
Quando chega um token com kid novo, a biblioteca pode atualizar o JWKS. Um atacante pode enviar milhares de valores aleatórios para causar chamadas repetidas. Use cooldown, rate limit e cache negativo.
Rotação de chaves
Uma rotação segura:
- gere a nova chave;
- publique a chave pública no JWKS;
- aguarde caches reconhecerem;
- comece a assinar com o novo
kid; - mantenha a chave antiga publicada;
- aguarde todos os tokens antigos expirarem;
- remova a chave antiga.
Se remover a chave antiga imediatamente, tokens ainda válidos deixam de funcionar.
Tempo de sobreposição
A chave anterior precisa permanecer pelo menos durante:
vida máxima do token + tolerância de relógio + propagação de cacheRefresh tokens normalmente não são validados diretamente pelo JWKS da resource API; eles são processados no authorization server.
Cache-Control no endpoint
Cache-Control: public, max-age=300, stale-while-revalidate=600Valores dependem da política de rotação. Um cache muito longo atrasa novas chaves; um cache curto aumenta tráfego.
ETag
O endpoint pode retornar ETag:
ETag: "jwks-version-42"Clientes usam If-None-Match e recebem 304 quando nada mudou. Veja ETag e Cache HTTP no Node.js.
Publicando JWKS
Ao criar seu próprio emissor, exponha apenas chaves públicas:
import { exportJWK } from 'jose';
const publicJwk = await exportJWK(publicKey);
const jwk = {
...publicJwk,
kid: '2026-09-primary',
use: 'sig',
alg: 'RS256'
};Endpoint:
app.get('/.well-known/jwks.json', (req, res) => {
res
.set('Cache-Control', 'public, max-age=300')
.type('application/jwk-set+json')
.send({ keys: publicKeys });
});Nunca publique material privado
Em RSA, campos como d, p, q, dp, dq e qi são privados. Em chaves EC e OKP, d também é privado.
Faça um teste que rejeita qualquer chave publicada contendo componentes privados.
Armazenamento da chave privada
A chave usada para assinar deve permanecer em:
- KMS;
- HSM;
- secret manager com acesso restrito;
- arquivo protegido em ambiente controlado.
Consulte Gestão de Segredos no Node.js.
KMS e JWKS
Quando o KMS assina sem exportar a chave privada:
- busque a chave pública;
- converta para JWK;
- publique no JWKS;
- envie o digest ao KMS para assinatura;
- monte o JWS.
Teste o formato da assinatura, especialmente ECDSA, pois representações DER e JOSE diferem.
Chaves RSA
RS256 é amplamente suportado, mas chaves precisam de tamanho seguro e geração criptográfica. Não gere pares em cada inicialização do processo.
Chaves EC e EdDSA
Curvas elípticas produzem chaves e assinaturas menores. EdDSA oferece uma API moderna quando todos os consumidores suportam. A escolha deve considerar interoperabilidade, bibliotecas e política.
use e key_ops
Uma chave de assinatura pública pode usar:
{
"use": "sig",
"key_ops": ["verify"]
}A RFC recomenda não usar os dois quando desnecessário; quando ambos existem, precisam ser consistentes.
Alg no JWK
alg indica o algoritmo pretendido. O validador ainda deve aplicar uma política própria. Não aceite uma chave RSA para qualquer algoritmo apenas porque o tipo é compatível.
Vários issuers
const validators = new Map([
['https://corp.example.com', corporateValidator],
['https://customers.example.com', customersValidator]
]);Primeiro extraia apenas o issuer de forma não confiável, use-o para encontrar uma configuração permitida e então valide integralmente. Nunca confie nos claims antes da assinatura.
Tokens sem kid
Um JWKS com uma única chave pode permitir validação, mas rotação se torna difícil. Exija kid em sistemas que usam múltiplas chaves.
Kid duplicado
Duas chaves com o mesmo kid tornam a seleção ambígua. O pipeline deve validar unicidade antes de publicar.
Fallback durante indisponibilidade
Use chaves em cache enquanto ainda são confiáveis. Não aceite tokens sem assinatura porque o endpoint caiu. Segurança sensível deve falhar fechada quando não existe chave válida.
Timeout
Defina timeout de rede no fetch usado pelo remote JWKS. Uma requisição de autenticação não pode ficar presa esperando indefinidamente.
Observabilidade
Monitore:
- cache hit e miss;
- atualizações do JWKS;
kiddesconhecido;- falhas de rede;
- assinatura inválida;
- issuer e audience inválidos;
- algoritmo rejeitado;
- tempo até reconhecer uma nova chave.
Não registre o token completo.
Testes
Cubra:
- token com chave atual;
- token com chave anterior;
- novo
kidapós refresh; kiddesconhecido;- algoritmo não permitido;
- issuer falso;
- audience errada;
- endpoint indisponível com cache;
- JWKS contendo chave privada;
- kid duplicado.
Erros comuns
- Confiar apenas na assinatura: issuer e audience ficam sem validação.
- URL do token: atacante fornece sua própria chave.
- Sem cache: autenticação depende de uma chamada por request.
- Cache eterno: rotação não chega.
- Remover chave cedo: tokens válidos quebram.
- Publicar d: chave privada é exposta.
- Algoritmo livre: política criptográfica é ignorada.
- Fail-open: tokens sem validação são aceitos.
Conclusão
O JWKS no Node.js distribui chaves públicas para validação de JWTs e permite rotação sem copiar certificados manualmente. kid, cache e sobreposição de chaves mantêm tokens antigos válidos durante a transição.
Configure a URL a partir de um issuer confiável, aplique allowlist de algoritmos e valide issuer, audience e tempo. Publique somente material público e proteja a chave privada em KMS ou secret manager. Com cache, cooldown e testes de rotação, JWKS oferece flexibilidade sem reduzir a confiança criptográfica.



