Cuando un JWT se decodifica bien, no está expirado y hasta puede tener una firma válida, pero la API responde invalid audience, audience mismatch o un 401 genérico, trato aud como una validación independiente. Una firma correcta prueba integridad y vinculación con una clave confiable; no prueba que el token esté destinado a esta API.
Si ya conozco el audience esperado, empiezo con JWT Debugger y después reviso cómo obtuvo el token el cliente.
Qué significa realmente aud
RFC 7519 permite que aud sea un string o un array de strings:
{ "aud": "api://orders" }
{ "aud": ["api://orders", "api://reporting"] }
La API debe comprobar que su identificador esperado aparece en ese valor. No invento normalizaciones como convertir a minúsculas, recortar rutas o hacer startsWith() salvo que el perfil usado lo defina expresamente.
Comparo Expected Audience y Actual Audience antes de tocar nada
API espera: api://orders-prod
Token aud: api://orders-dev
Resultado: mismatch
Si aud es un array, uso pertenencia:
function audienceMatches(aud, expected) {
if (typeof aud === "string") return aud === expected;
if (Array.isArray(aud)) return aud.includes(expected);
return false;
}
expected viene de configuración confiable del servidor, nunca del propio JWT.
Causa frecuente: el cliente pidió un token para otra resource
El login puede funcionar y aun así obtenerse un token para el recurso equivocado. Busco patrones como:
- el cliente solicita scopes de
billingpero llama aorders; - el API identifier de producción no coincide con el solicitado;
- se reutilizó configuración de staging;
- el gateway espera una resource URI y el cliente usa otro identificador;
- el frontend conserva un token antiguo para otra API.
La corrección está en el request OAuth/OIDC o en la configuración del recurso, no en desactivar la validación.
Causa frecuente: enviar un ID Token como si fuera Access Token
Un ID Token de OpenID Connect está orientado al cliente que inició la autenticación. Su aud suele identificar a ese cliente. Una resource API normalmente espera un Access Token dirigido a ella.
Si veo:
{
"iss": "https://login.example.com/",
"aud": "web-client-123",
"sub": "user-42"
}
pero la API espera api://orders, compruebo qué variable se envía realmente en Authorization: Bearer.
Causa frecuente: mezclar dev, staging y producción
Analizo iss y aud juntos:
expected iss = https://login.example.com/prod
expected aud = api://orders-prod
actual iss = https://login.example.com/dev
actual aud = api://orders-dev
Si ambos apuntan a dev, el problema casi siempre está en environment/tenant/configuración, no en criptografía.
Mensajes de error que asocio con esta capa
Los frameworks usan textos distintos:
invalid audience
audience mismatch
token audience is invalid
audience not allowed
unexpected "aud" claim value
No me quedo con el mensaje: decodifico aud y lo comparo con la configuración real del servidor.
Por qué no desactivo audience validation
Un mismo issuer puede emitir tokens para varias APIs. Si Service B acepta un token emitido para Service A, se rompe el límite entre recursos. RFC 8725 recomienda validar audience cuando el perfil la utiliza.
Por eso un mismatch no se arregla con un wildcard ni aceptando cualquier aud.
Editar aud tampoco arregla el token
Modificar el payload cambia los bytes firmados. La firma anterior debe dejar de verificar. El arreglo legítimo está en:
- resource/audience solicitada;
- scopes del request;
- Expected Audience de la API;
- environment/tenant;
- selección entre Access Token e ID Token.
Para la capa criptográfica uso JWT Signature Verifier.
Mi matriz de decisión
| Observación | Causa probable | Acción |
|---|---|---|
aud es client ID | ID Token usado en API | revisar token enviado |
aud apunta a dev | mezcla de environments | corregir client/API config |
aud es array | comparación incorrecta | comprobar membership |
firma válida pero aud incorrecto | resource equivocada | pedir token para API correcta |
iss y aud incorrectos | tenant/issuer equivocado | revisar discovery/config |
Mi flujo de depuración
- Conservo el JWT original sin editar.
- Leo
iss,aud,azp,scopeyexpcon JWT Decoder. - Obtengo Expected Audience desde la configuración de la API.
- Trato
audcorrectamente como string o array. - Verifico que se envía un Access Token y no un ID Token.
- Comparo dev/staging/prod y tenant en cliente y servidor.
- Reviso resource/scope/audience del token request.
- Confirmo el issuer esperado y su fuente de claves confiable.
- Verifico la firma si aún no lo hice.
- Corrijo acquisition/configuración y solicito un token nuevo.
Si el fallo no se limita a aud, continúo con la guía de JWT 401. La idea central es: un token auténtico no es automáticamente válido para cualquier destinatario.