{"record":{"id":"602957e63ef4ccf6","repo":"honojs/hono","slug":"required-kid-in-jwt-header-json-stringify-hea","errorCode":null,"errorMessage":"required \"kid\" in jwt header: ${JSON.stringify(header)}","messagePattern":"required \"kid\" in jwt header: (.+?)","errorType":"exception","errorClass":"JwtHeaderRequiresKid","httpStatus":null,"severity":"error","filePath":"src/utils/jwt/jwt.ts","lineNumber":215,"sourceCode":"export const verifyWithJwks = async (\n  token: string,\n  options: {\n    keys?: HonoJsonWebKey[]\n    jwks_uri?: string\n    verification?: VerifyOptions\n    allowedAlgorithms: readonly AsymmetricAlgorithm[]\n  },\n  init?: RequestInit\n): Promise<JWTPayload> => {\n  const verifyOpts = options.verification || {}\n\n  const header = decodeHeader(token)\n\n  if (!isTokenHeader(header)) {\n    throw new JwtHeaderInvalid(header)\n  }\n  if (!header.kid) {\n    throw new JwtHeaderRequiresKid(header)\n  }\n\n  // Reject symmetric algorithms (HS256, HS384, HS512) to prevent algorithm confusion attacks\n  if (symmetricAlgorithms.includes(header.alg as SymmetricAlgorithm)) {\n    throw new JwtSymmetricAlgorithmNotAllowed(header.alg)\n  }\n\n  // Validate against allowed algorithms\n  if (!options.allowedAlgorithms.includes(header.alg as AsymmetricAlgorithm)) {\n    throw new JwtAlgorithmNotAllowed(header.alg, options.allowedAlgorithms)\n  }\n\n  let verifyKeys = options.keys ? [...options.keys] : undefined\n\n  if (options.jwks_uri) {\n    const response = await fetch(options.jwks_uri, init)\n    if (!response.ok) {\n      throw new Error(`failed to fetch JWKS from ${options.jwks_uri}`)","sourceCodeStart":197,"sourceCodeEnd":233,"githubUrl":"https://github.com/honojs/hono/blob/e2740d5a1bd0b4254e517e3af8b60789284bc7bd/src/utils/jwt/jwt.ts#L197-L233","documentation":"Thrown by verifyWithJwks when the JWT header is structurally valid but has no kid (key ID) claim. JWK-based verification requires kid to select which key from the JWKS/keys list is used.","triggerScenarios":"verifyWithJwks(token, options) on a token whose header omits kid — e.g. a symmetric-HS256 token or a custom-minted token without kid.","commonSituations":"Using verifyWithJwks for tokens signed with a shared secret (no kid); tokens from a legacy issuer that doesn't set kid; testing with manually crafted tokens; confusion between verify() (single fixed key) and verifyWithJwks() (key lookup by kid).","solutions":["If the token is symmetric/shared-secret, use verify() with the secret key instead of verifyWithJwks","If using an asymmetric issuer, ensure it includes kid in the JOSE header (most OIDC providers do)","If you already know the exact key, pass it to verify() directly and skip JWKS lookup","Check whether the JWKS has only one key — some libraries allow kid-less selection, but this one requires kid"],"exampleFix":"// before\nawait verifyWithJwks(hs256TokenWithoutKid, opts) // throws\n// after\nawait verify(hs256TokenWithoutKid, secret, { alg: 'HS256' })","handlingStrategy":"validation","validationCode":"const header = decodeHeader(token)\nif (!header.kid) { /* route to verify() with shared secret or reject */ }","typeGuard":"const hasKid = (h: unknown): h is { kid: string } =>\n  typeof h === 'object' && h !== null && typeof (h as { kid?: unknown }).kid === 'string'","tryCatchPattern":"try { await verifyWithJwks(token, opts) } catch (e) { if (e instanceof JwtHeaderRequiresKid) return unauthorized('missing kid'); throw e }","preventionTips":["Use verifyWithJwks only for asymmetric tokens with kid","Keep symmetric tokens on verify() with the secret"],"tags":["jwt","jwks","key-id","header-validation"],"backgroundTag":"jwt-missing-kid","analyzedSha":"e2740d5a1bd0b4254e517e3af8b60789284bc7bd","analyzedAt":"2026-08-28T10:18:08.750Z","schemaVersion":2},"datasetVersion":"2026-08-28T11:17:15.048Z"}