honojs/hono · critical · JwtSymmetricAlgorithmNotAllowed

symmetric algorithm "${alg}" is not allowed for JWK verifica

Error message

symmetric algorithm "${alg}" is not allowed for JWK verification

What it means

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.

Source

Thrown at src/utils/jwt/jwt.ts:220

    verification?: VerifyOptions
    allowedAlgorithms: readonly AsymmetricAlgorithm[]
  },
  init?: RequestInit
): Promise<JWTPayload> => {
  const verifyOpts = options.verification || {}

  const header = decodeHeader(token)

  if (!isTokenHeader(header)) {
    throw new JwtHeaderInvalid(header)
  }
  if (!header.kid) {
    throw new JwtHeaderRequiresKid(header)
  }

  // Reject symmetric algorithms (HS256, HS384, HS512) to prevent algorithm confusion attacks
  if (symmetricAlgorithms.includes(header.alg as SymmetricAlgorithm)) {
    throw new JwtSymmetricAlgorithmNotAllowed(header.alg)
  }

  // Validate against allowed algorithms
  if (!options.allowedAlgorithms.includes(header.alg as AsymmetricAlgorithm)) {
    throw new JwtAlgorithmNotAllowed(header.alg, options.allowedAlgorithms)
  }

  let verifyKeys = options.keys ? [...options.keys] : undefined

  if (options.jwks_uri) {
    const response = await fetch(options.jwks_uri, init)
    if (!response.ok) {
      throw new Error(`failed to fetch JWKS from ${options.jwks_uri}`)
    }
    const data = (await response.json()) as { keys?: JsonWebKey[] }
    if (!data.keys) {
      throw new Error('invalid JWKS response. "keys" field is missing')
    }

View on GitHub (pinned to e2740d5a1b)

Solutions

  1. Use asymmetric tokens (RS256/ES256/...) with verifyWithJwks; keep HS* tokens on verify() with the shared secret
  2. Never 'fix' this by loosening the check — the rejection is a security feature
  3. If the IdP supports RS256, switch the application/client configuration to it

Example fix

// before
await verifyWithJwks(hs256Token, { allowedAlgorithms: ['HS256'], ... }) // throws by design
// after
await verifyWithJwks(rs256Token, { allowedAlgorithms: ['RS256'], ... })
Defensive patterns

Strategy: validation

Validate before calling

const SYMMETRIC = ['HS256','HS384','HS512']
if (SYMMETRIC.includes(decodeHeader(token).alg)) { /* use verify() with secret, not JWKS */ }

Try / catch

try { await verifyWithJwks(token, opts) } catch (e) { if (e instanceof JwtSymmetricAlgorithmNotAllowed) return unauthorized(); throw e }

Prevention

When it happens

Trigger: verifyWithJwks receives a token whose header alg is HS256, HS384, or HS512 (token signed with a shared secret).

Common situations: 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.

Related errors


AI-assisted analysis of honojs/hono@e2740d5a1b (2026-08-28). Data as JSON: /api/errors/7cc8d4bd86b0e020. Report an issue: GitHub.