Mensaje de error
unexpected "aud" claim value
jose (JavaScript) — JWTClaimValidationFailed, code ERR_JWT_CLAIM_VALIDATION_FAILED
Qué significa
La firma se verificó, pero el claim (declaración) `aud` del token no contiene el valor que pasas como `audience` a `jwtVerify`. jose compara las cadenas de forma exacta: la comprobación se supera cuando al menos un valor del `aud` del token (una cadena o un array) es igual a una de las audiencias que permites. El token es auténtico; simplemente se emitió para otro destinatario, así que aceptarlo permitiría que un token emitido para una API se reutilizara contra otra.
Por qué ocurre
El token se emitió para otra API
frecuenteUn mismo servidor de autorización suele dar servicio a varios servidores de recursos. Un cliente que obtiene un token para la API de pedidos y lo envía a la API de facturación recibe exactamente este error: la comprobación está haciendo su trabajo. La propiedad `err.payload` del error muestra los claims que lleva realmente el token.
La misma API, con el identificador escrito de otra forma
frecuenteLa comparación de la audiencia es exacta byte a byte: `https://api.example.com` y `https://api.example.com/` (barra final), http frente a https, o un nombre lógico como `api://orders` frente a la URL de la API son, en todos los casos, valores distintos. Los proveedores de identidad copian el identificador tal cual de su configuración, así que el verificador debe escribirlo exactamente igual.
Se envía un token de ID donde se espera un token de acceso
ocasionalEn OpenID Connect, el `aud` del token de ID es el ID de cliente de la aplicación con la que el usuario inició sesión, no tu API. Por eso, los frontends que reenvían el token de ID en lugar del token de acceso no superan la comprobación de audiencia de ninguna de las API que tienen detrás.
El cliente no pidió una audiencia concreta
ocasionalVarios proveedores (Auth0, los servidores de autorización personalizados de Okta, Keycloak con mapeadores de audiencia) solo ponen tu API en `aud` cuando la solicitud del token la nombra. Sin el parámetro `audience` o el parámetro `resource` del RFC 8707, el token se emite para una audiencia predeterminada, como el endpoint userinfo.
Cómo solucionarlo
- 1.
Imprimir lo que lleva el token junto a lo que esperas
Decodifica los claims sin confiar en ellos y compara `aud` con tu opción `audience` carácter a carácter. Corrige el lado que esté mal —la opción del verificador o la configuración del emisor—, pero mantén la comprobación.
js import { decodeJwt, jwtVerify } from "jose"; console.log("token aud:", decodeJwt(token).aud); // string or array of strings await jwtVerify(token, jwks, { algorithms: ["ES256"], issuer: "https://issuer.example.com", audience: "https://api.example.com/orders", // exact string match }); - 2.
Solicitar un token para la API a la que llamas
Cuando el token se emitió para otra audiencia, la corrección corresponde al cliente: indica la API de destino en la solicitud del token para que el servidor de autorización la ponga en `aud`.
bash # OAuth 2.0 client credentials: ask the authorization server for a token # minted for the API you are about to call (Auth0 uses "audience", # RFC 8707 resource indicators use "resource"). curl -s https://issuer.example.com/oauth/token \ -d grant_type=client_credentials \ -d client_id="$CLIENT_ID" -d client_secret="$CLIENT_SECRET" \ -d audience=https://api.example.com/orders - 3.
Permitir varios identificadores de forma explícita, si todos son tuyos
`audience` acepta un array. Enumera todos los identificadores que designan legítimamente a esta API (por ejemplo, durante una migración de una URL a un nombre lógico) en lugar de quitar la opción: sin `audience`, jose no comprueba `aud` en absoluto.
js await jwtVerify(token, jwks, { algorithms: ["ES256"], audience: ["https://api.example.com/orders", "orders-api"], // any one must match });