{"record":{"id":"7cc8d4bd86b0e020","repo":"honojs/hono","slug":"symmetric-algorithm-alg-is-not-allowed-for-jw","errorCode":null,"errorMessage":"symmetric algorithm \"${alg}\" is not allowed for JWK verification","messagePattern":"symmetric algorithm \"(.+?)\" is not allowed for JWK verification","errorType":"exception","errorClass":"JwtSymmetricAlgorithmNotAllowed","httpStatus":null,"severity":"critical","filePath":"src/utils/jwt/jwt.ts","lineNumber":220,"sourceCode":"    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}`)\n    }\n    const data = (await response.json()) as { keys?: JsonWebKey[] }\n    if (!data.keys) {\n      throw new Error('invalid JWKS response. \"keys\" field is missing')\n    }","sourceCodeStart":202,"sourceCodeEnd":238,"githubUrl":"https://github.com/honojs/hono/blob/e2740d5a1bd0b4254e517e3af8b60789284bc7bd/src/utils/jwt/jwt.ts#L202-L238","documentation":"A deliberate security guard in verifyWithJwks: symmetric algorithms (HS256/HS384/HS512) are rejected because JWKS-based verification would let an attacker-supplied HS256 token be 'verified' with a public key as the HMAC secret, enabling algorithm-confusion attacks.","triggerScenarios":"verifyWithJwks receives a token whose header alg is HS256, HS384, or HS512 (token signed with a shared secret).","commonSituations":"Pointing verifyWithJwks at tokens from a legacy symmetric issuer; attackers crafting alg:none/HS256 tokens against JWKS endpoints; migrating code from verify() to verifyWithJwks without changing token type.","solutions":["Use asymmetric tokens (RS256/ES256/...) with verifyWithJwks; keep HS* tokens on verify() with the shared secret","Never 'fix' this by loosening the check — the rejection is a security feature","If the IdP supports RS256, switch the application/client configuration to it"],"exampleFix":"// before\nawait verifyWithJwks(hs256Token, { allowedAlgorithms: ['HS256'], ... }) // throws by design\n// after\nawait verifyWithJwks(rs256Token, { allowedAlgorithms: ['RS256'], ... })","handlingStrategy":"validation","validationCode":"const SYMMETRIC = ['HS256','HS384','HS512']\nif (SYMMETRIC.includes(decodeHeader(token).alg)) { /* use verify() with secret, not JWKS */ }","typeGuard":null,"tryCatchPattern":"try { await verifyWithJwks(token, opts) } catch (e) { if (e instanceof JwtSymmetricAlgorithmNotAllowed) return unauthorized(); throw e }","preventionTips":["Never attempt to verify HS* tokens via JWKS","Restrict allowedAlgorithms to asymmetric families"],"tags":["jwt","security","algorithm-confusion","jwks"],"backgroundTag":"jwt-algorithm-confusion","analyzedSha":"e2740d5a1bd0b4254e517e3af8b60789284bc7bd","analyzedAt":"2026-08-28T10:18:08.750Z","schemaVersion":2},"datasetVersion":"2026-08-28T11:17:15.048Z"}