Перейти к содержимому
ecdsa.com
jose (JavaScript)Библиотеки JWT

Сообщение об ошибке

unexpected "iss" claim value

jose (JavaScript) — JWTClaimValidationFailed, code ERR_JWT_CLAIM_VALIDATION_FAILED

Что это означает

Криптографическая проверка токена прошла, но его утверждение `iss` не равно параметру `issuer`, который вы передали в `jwtVerify`. jose сравнивает строки точно (можно передать и массив разрешённых издателей), поэтому любое расхождение — косая черта в конце, другой путь тенанта, другое окружение — приводит к отказу. Именно закрепление издателя не даёт принять токен от другого поставщика удостоверений или от другого тенанта того же провайдера.

Почему это происходит

Как исправить

  1. 1.

    Берите строку издателя из документа обнаружения

    По спецификации значение `issuer` в `/.well-known/openid-configuration` — ровно то, что провайдер записывает в `iss`. Скопируйте его в свою конфигурацию байт в байт.

    bash
    # The issuer string is defined by the provider's discovery document.
    curl -s https://login.example.com/.well-known/openid-configuration | jq -r .issuer
  2. 2.

    Закрепите именно это значение в jwtVerify

    Не убирайте параметр `issuer` (без него jose вообще не проверяет `iss`) и храните значение в одной константе, общей для URL JWKS и проверяющей стороны.

    js
    import { createRemoteJWKSet, jwtVerify } from "jose";
    
    const ISSUER = "https://login.example.com/"; // exactly as in "issuer" from discovery
    const jwks = createRemoteJWKSet(new URL("https://login.example.com/.well-known/jwks.json"));
    
    const { payload } = await jwtVerify(token, jwks, { algorithms: ["ES256"], issuer: ISSUER });
  3. 3.

    Принимайте нескольких издателей, у каждого — свой набор ключей

    Если ваш сервис действительно доверяет нескольким издателям, выбирайте набор ключей по (ещё не проверенному) `iss` токена из явного списка разрешённых и затем проверяйте токен, закрепив этого издателя. Никогда не позволяйте проверять токены одного издателя ключами другого.

    js
    import { createRemoteJWKSet, decodeJwt, jwtVerify } from "jose";
    
    // Each trusted issuer gets its own key set; an unknown iss is rejected before any key is used.
    const trusted = new Map([
      ["https://login.example.com/", createRemoteJWKSet(new URL("https://login.example.com/.well-known/jwks.json"))],
      ["https://partner.example.org", createRemoteJWKSet(new URL("https://partner.example.org/jwks"))],
    ]);
    
    const { iss } = decodeJwt(token);
    const keys = trusted.get(iss);
    if (!keys) throw new Error("untrusted issuer");
    await jwtVerify(token, keys, { algorithms: ["ES256", "RS256"], issuer: iss });

Связанные ошибки

Открыть всю базу ошибок подписей