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
- 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
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
- Never attempt to verify HS* tokens via JWKS
- Restrict allowedAlgorithms to asymmetric families
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
- token(${token}) signature mismatched
- required "kid" in jwt header: ${JSON.stringify(header)}
- algorithm "${alg}" is not in the allowed list: [${allowedAlg
- invalid JWKS response. "keys" field is missing
- invalid JWKS response. "keys" field is not an array
AI-assisted analysis of honojs/hono@e2740d5a1b (2026-08-28).
Data as JSON: /api/errors/7cc8d4bd86b0e020.
Report an issue: GitHub.