Skip to content

fix(jwt): filter JWKS keys by signature use and key_ops in verifyWithJwks - #5452

Open
agustin18 wants to merge 1 commit into
honojs:mainfrom
agustin18:fix/jwt-jwks-filter-use-key-ops
Open

agustin18 wants to merge 1 commit into
honojs:mainfrom
agustin18:fix/jwt-jwks-filter-use-key-ops

Conversation

@agustin18

Copy link
Copy Markdown

The author should do the following, if applicable

  • Add tests
  • Run tests
  • pnpm run check:fix to format and lint the code
  • Add TSDoc/JSDoc to document the code (no public API signature change)

Problem

Per RFC 7517 §4.2 (use) and §4.3 (key_ops), JWKs can specify their intended public key usage:

  • use: "sig" (signature verification) or "enc" (encryption).
  • key_ops: an array of key operation values (e.g. ["verify"], ["encrypt"]).

In verifyWithJwks(), candidate key selection previously only filtered by key.kid === header.kid:

const matchingKey = verifyKeys.find((key) => key.kid === header.kid)

When a JWKS contains both an encryption key and a signature key sharing the same kid (standard in dual-use certificates and enterprise identity providers such as Okta, Azure AD, and Cognito), or when a key with the requested kid has use: "enc" or lacks "verify" in key_ops, verifyKeys.find() would select the encryption key. Subsequently, crypto.subtle.importKey("jwk", key, alg, false, ["verify"]) throws an uncaught DOMException: DataError (Invalid JWK "use" Parameter or Key operations and usage mismatch) rather than selecting the valid signing key or throwing JwtTokenInvalid(token).

Solution

Filter candidate keys in verifyWithJwks() to only select keys compatible with asymmetric signature verification per RFC 7517:

  1. key.use === undefined || key.use === 'sig'
  2. key.key_ops === undefined || (Array.isArray(key.key_ops) && key.key_ops.includes('verify'))
  3. If no key with compatible usage exists for the kid, verifyWithJwks() cleanly rejects with JwtTokenInvalid(token) instead of throwing an unhandled DataError DOMException.
  4. Keys omitting use or key_ops remain accepted, maintaining full backwards compatibility.

Tests

Added test cases to src/utils/jwt/jwt.test.ts under verifyWithJwks key handling:

  • Selects use: "sig" key when both encryption and signature keys share the same kid.
  • Selects key_ops: ["verify"] key when both encryption and verification keys share the same kid.
  • Throws JwtTokenInvalid when matching kid only has use: "enc".
  • Throws JwtTokenInvalid when matching kid only has key_ops: ["encrypt"].
  • All 94 tests in src/utils/jwt/jwt.test.ts pass cleanly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant