Guías JWT

Cómo distingo JWK y JWKS al verificar JWT

Diferencia entre una JWK y un JWKS, selección por kid, jwks_uri, rotación de claves y el trust path seguro hasta verificar la firma.

Por JWTDecoder.toolsÚltima verificación 2026-09-16

Voz editorial: la primera persona describe el flujo de trabajo documentado de JWTDecoder.tools; JWTDecoder.tools es el editor y autor organizacional.

Diagrama de issuer confiable, JWKS, kid y JWK
Un issuer confiable publica JWKS; kid selecciona la JWK usada para verificar la firma.

La diferencia básica es sencilla: JWK describe una clave criptográfica en JSON; JWKS es un conjunto de JWKs. La diferencia práctica aparece al seleccionar claves con kid, rotarlas, descubrir jwks_uri, cachearlas y vincularlas con un issuer confiable.

Uso JWK Inspector para una clave individual y JWKS Validator para validar un token contra un set.

Una JWK representa una clave

{
  "kty": "RSA",
  "kid": "prod-2026-09",
  "use": "sig",
  "alg": "RS256",
  "n": "...",
  "e": "AQAB"
}

Campos relevantes:

  • kty: tipo de clave;
  • kid: identificador de selección;
  • use: uso previsto;
  • key_ops: operaciones permitidas;
  • alg: algoritmo declarado;
  • RSA: n, e;
  • EC: crv, x, y.

kid no demuestra confianza; ayuda a elegir candidato.

Un JWKS contiene varias JWKs

{
  "keys": [
    { "kty": "RSA", "kid": "old", "alg": "RS256", "use": "sig", "n": "...", "e": "AQAB" },
    { "kty": "RSA", "kid": "new", "alg": "RS256", "use": "sig", "n": "...", "e": "AQAB" }
  ]
}

Durante key rotation es normal que convivan claves old/new.

Cómo uso kid para seleccionar

JWT kid
  ↓
buscar en JWKS
  ↓
filtrar kty/use/key_ops/alg
  ↓
verificar firma

Si no hay match, pregunto si el JWKS está actualizado y si pertenece al issuer esperado. No elijo la primera clave disponible.

jwks_uri debe formar parte de un trust path

expected issuer
  ↓
metadata/discovery confiable
  ↓
jwks_uri
  ↓
JWKS
  ↓
matching kid
  ↓
signature verification

No sigo automáticamente una jku arbitraria del JWT header y confío en la clave que aparezca allí.

Compatibilidad entre alg y kty

Un match de kid no basta. Espero combinaciones coherentes:

RS256 -> RSA
ES256 -> EC P-256

También respeto use: sig y key_ops cuando están presentes.

Missing kid y duplicate kid

Casos que trato explícitamente:

  • token tiene kid, set no tiene match;
  • varios keys comparten kid;
  • token no tiene kid y hay varios candidatos;
  • match tiene kty o alg incompatible;
  • old key desapareció antes de expirar tokens emitidos.

Ambigüedad es error, no una invitación a probar claves al azar.

Verificación con jose y un remote JWKS

En Node.js, un patrón útil es fijar el issuer y audience desde configuración y dejar que la librería resuelva la clave dentro del JWKS de confianza:

import { createRemoteJWKSet, jwtVerify } from "jose";

const issuer = "https://login.example.com/";
const jwks = createRemoteJWKSet(
  new URL("https://login.example.com/.well-known/jwks.json")
);

const { payload, protectedHeader } = await jwtVerify(token, jwks, {
  issuer,
  audience: "api://orders",
  algorithms: ["RS256"],
});

Lo importante no es copiar este snippet literalmente, sino mantener tres límites claros: URL de confianza, algorithm allowlist y claims esperados.

Cache HTTP y refresh ante unknown kid

Un JWKS endpoint puede responder con headers como:

Cache-Control: public, max-age=300
ETag: "jwks-v42"

No fuerzo un fetch por request. Tampoco ignoro para siempre una nueva clave. Ante kid desconocido, un verifier robusto suele hacer un refresh controlado, respetar cache/rate limits y volver a intentar solo contra el mismo issuer/JWKS de confianza.

Errores que quiero distinguir:

unknown kid + JWKS stale       -> refresh controlado
unknown kid + JWKS fresh       -> token/issuer sospechoso
JWKS fetch 5xx                 -> dependencia temporal
JWKS timeout                   -> red/issuer
kid duplicado                  -> configuración ambigua

Qué observo en producción

Para no convertir una rotación normal en un incidente opaco, mido:

  • unknown_kid por issuer;
  • refreshes de JWKS;
  • latencia y errores HTTP del jwks_uri;
  • kid más visto por ventana de tiempo;
  • verificaciones que siguen usando la old key durante overlap.

No registro private keys ni JWTs completos. Un pico de unknown_kid justo tras publicar una new key suele apuntar a cache; un pico sin rotación puede apuntar a tokens del issuer equivocado.

Cache y key rotation

No quiero descargar JWKS en cada request, pero tampoco cachearlo para siempre. Un patrón útil es cache + refresh controlado ante un kid desconocido, con rate limiting y sin cambiar la fuente confiable.

La guía de key rotation explica overlap y stale cache.

JWK Thumbprint no es kid

RFC 7638 define un thumbprint canónico calculado desde miembros concretos de la JWK. kid es un identificador elegido por el emisor. El thumbprint ayuda a comparar material de clave, pero no sustituye issuer/trust policy.

JWK Inspector calcula el thumbprint localmente.

Tests negativos que protegen la selección

kid conocido + RS256 + RSA     -> verificar
kid desconocido                -> refresh controlado o reject
kid duplicado                  -> reject por ambigüedad
kid match + kty incompatible   -> reject
jku arbitraria en header       -> no cambiar trust source
JWKS de otro issuer            -> reject aunque la firma cuadre

Esta batería evita que un fallo de selección se convierta en “probar cualquier clave hasta que una funcione”.

Matriz JWK vs JWKS

PreguntaJWKJWKS
¿Una sola clave?síno, set
¿Tiene keys array?nosí
¿Soporta overlap de rotación?no por sí solasí
¿Selección por kid?opcionalfrecuente
¿Se publica por jwks_uri?no directamentenormalmente

Mi workflow seguro

  1. Obtengo expected issuer desde configuración confiable.
  2. Uso su metadata/JWKS conocido.
  3. Leo alg y kid del JWT solo como hints.
  4. Selecciono el kid correcto.
  5. Filtro kty, use, key_ops, alg y curve cuando aplica.
  6. Rechazo selección ausente o ambigua.
  7. Verifico la firma sobre el compact JWT original.
  8. Después valido iss, aud, exp y demás claims.
  9. Ante unknown kid, investigo cache/rotation sin cambiar el trust source.
  10. Correlaciono unknown-kid y JWKS refresh con métricas para confirmar la causa.

JWKS no es solo «muchas JWK». Es una pieza del ciclo de descubrimiento, selección, cache y rotación de claves.

Fuentes y referencias

Guías relacionadas