Перейти к содержимому
ecdsa.com
jose (JavaScript)JWKS и обнаружение ключей

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

multiple matching keys found in the JSON Web Key Set

jose (JavaScript) — JWKSMultipleMatchingKeys, code ERR_JWKS_MULTIPLE_MATCHING_KEYS

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

В наборе ключей JSON Web Key Set нашлось больше одного ключа, которым можно проверить этот токен, и jose отказывается угадывать. Так бывает, когда в заголовке токена нет `kid` (или у нескольких ключей один и тот же `kid`), а в наборе есть два или больше ключей подходящего типа на подходящей кривой. jose намеренно не перебирает кандидатов за вас внутри `jwtVerify`: выброшенная ошибка сама служит асинхронным итератором по ключам-кандидатам, так что перебор можно включить явно.

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

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

  1. 1.

    Указывайте ключ подписи в каждом токене

    Надёжное исправление — на стороне издателя: присвойте каждому ключу уникальный `kid` (хорошо подходит отпечаток RFC 7638), опубликуйте его в JWKS и записывайте в защищённый заголовок каждого токена.

    js
    import { SignJWT } from "jose";
    
    // Issuer side: name the key in every token, and publish the same kid in the JWKS.
    const token = await new SignJWT(claims)
      .setProtectedHeader({ alg: "ES256", kid: "2026-10" })
      .setExpirationTime("10m")
      .sign(privateKey);
  2. 2.

    Включите перебор кандидатов явно

    Если изменить издателя нельзя, переберите ошибку как итератор: jose по очереди выдаёт каждый ключ-кандидат, и вы принимаете токен, только если один из этих ключей подтверждает подпись. Каждый кандидат обходится в одну проверку подписи, поэтому держите набор ключей небольшим.

    js
    import { errors, jwtVerify } from "jose";
    
    async function verifyWithCandidates(token, jwks, options) {
      try {
        return await jwtVerify(token, jwks, options);
      } catch (err) {
        if (err?.code !== "ERR_JWKS_MULTIPLE_MATCHING_KEYS") throw err;
        for await (const publicKey of err) {
          try {
            return await jwtVerify(token, publicKey, options);
          } catch (inner) {
            if (inner?.code === "ERR_JWS_SIGNATURE_VERIFICATION_FAILED") continue;
            throw inner;
          }
        }
        throw new errors.JWSSignatureVerificationFailed();
      }
    }

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

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