Cuando un JWT parece correcto pero la API responde invalid issuer, issuer mismatch o 401 incluso después de una firma válida, trato iss como un problema del límite de confianza.
No me basta preguntar qué cadena hay en iss. Quiero saber:
¿Es exactamente el issuer que mi aplicación configuró como confiable y verifiqué el token con claves que realmente pertenecen a ese issuer?
RFC 8725 hace importante esa segunda parte.
Paso 1: leo iss sin confiar todavía
{
"iss": "https://login.example.com/",
"sub": "user_123",
"aud": "api://orders",
"exp": 1789203600
}
Antes de verificar la firma, un atacante puede escribir cualquier issuer en el payload. Lo uso como pista, no como prueba.
Paso 2: identifico el issuer esperado
Busco una única fuente autoritativa de configuración, por ejemplo:
https://login.example.com/
Y comparo exactamente.
Valores parecidos pueden ser distintos:
https://login.example.com
https://login.example.com/
https://login.example.com/tenant-a
https://login.example.com/tenant-b
No normalizo silenciosamente si el protocolo o la librería esperan coincidencia exacta.
Paso 3: descarto mezclas entre dev, staging y prod
Un token de desarrollo puede tener:
{
"iss": "https://login.dev.example.com/",
"aud": "api://orders-dev"
}
mientras producción espera otro issuer. Eso puede producir issuer mismatch, audience mismatch, kid not found y fallo de firma al mismo tiempo.
Si fallan issuer y kid, miro el entorno antes que el código criptográfico.
Paso 4: vinculo el conjunto de claves al issuer
Con RFC 8414 quiero este camino:
la aplicación confía en issuer A
↓
metadata de issuer A
↓
jwks_uri de issuer A
↓
clave del JWKS de issuer A
↓
firma verificada
No quiero:
un token no verificado dice iss=A
→ el token indica una URL arbitraria
→ acepto la clave que aparezca allí
El token sigue siendo entrada no confiable.
JWKS Validator requiere que yo proporcione el JWKS de confianza y no sigue automáticamente jku/x5u.
Paso 5: verifico la firma antes de confiar en claims
Si kid está presente, lo selecciono dentro del JWKS ligado al issuer de confianza y verifico la firma.
Si falta la clave sigo la guía de JWT kid; si existe pero la firma falla, sigo la guía de firma inválida.
La configuración del issuer determina qué claves puedo confiar; esas claves autentican después el iss firmado.
Paso 6: una firma válida no basta
Puedo tener:
firma válida ✓
iss == esperado ✕
y aun así rechazo.
Una plataforma puede tener muchos issuers, tenants o perfiles de token con claves criptográficamente válidas.
Paso 7: no confundo issuer y audience
iss= quién emitió.aud= para quién fue emitido.
Después de issuer sigo con audience, exp, nbf, tipo de token y permisos.
Paso 8: multi-tenant con cuidado
Reviso:
- issuer único por tenant;
- si la librería espera un issuer específico del tenant;
- si las claves se comparten;
- si la API admite múltiples issuers de forma intencionada;
- si la selección del issuer viene de configuración de confianza.
No acepto cualquier issuer de un hostname amplio salvo que el modelo de seguridad lo defina explícitamente.
Paso 9: reviso slash, path, scheme y espacios
Comparo las cadenas literalmente, incluyendo trailing slash, path del tenant, https, hostname y espacios accidentales.
Paso 10: comparo configuración, metadata y JWT
issuer configurado
issuer de metadata
JWT iss
RFC 8414 también proporciona jwks_uri. Si estos puntos no coinciden, sospecho discovery o configuración antes que la librería JWT.
Mi checklist
- Decodifico y leo
iss,kid,alg,aud. - Identifico el issuer esperado exacto.
- Reviso entorno y tenant.
- Obtengo metadata/JWKS desde configuración de confianza.
- Selecciono
kiddentro de ese JWKS. - Verifico la firma con una clave del issuer.
- Comparo exactamente el
issautenticado. - Valido
aud,exp,nbfy perfil de token. - Leo el error del servidor.
- Corrijo la cadena de confianza; no debilito la validación del issuer.
La validación del issuer no consiste en comprobar una URL familiar: consiste en autenticar el JWT con claves del issuer que elegí confiar y confirmar que el identificador firmado coincide exactamente con esa identidad.