跳到主要内容
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 密钥集中有不止一个密钥可以验证这个令牌,而 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();
      }
    }

相关错误

浏览完整的签名错误库