v0.14 is out: a mobile auth pack for Swift/iOS and Android, catching insecure token storage, cleartext traffic, and OAuth in embedded WebViews. Read more →
HIGH AI PREVALENCE: MEDIUM auth.jwt.algorithm-confusion

A JWT is being verified with HS256 (a symmetric algorithm) but the key looks like an RSA / EC public key in PEM format.

Why AI tools produce this: AI coding tools produce this regularly, typically when prompted for a shortcut or a quick fix.

Why this matters

This is the "algorithm confusion" attack: an attacker can sign forged tokens with the public key and your code will happily verify them with HMAC-SHA256 treating the PEM string as the shared secret.

Always pin the algorithm to the asymmetric one you actually use (e.g. algorithms: ['RS256']) and pass the public key only when verifying asymmetric tokens.

RFC 7518 §3.1: the "alg" header must be matched to the key type.

VULNERABLE
vulnerable.ts
import jwt from 'jsonwebtoken';

const RSA_PUBLIC = `-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxxxxxxxxxxxxxxxxxxxx
-----END PUBLIC KEY-----`;

// ruleid: auth.jwt.algorithm-confusion
export function badVerify(token: string) {
  return jwt.verify(token, RSA_PUBLIC, { algorithms: ['HS256'] });
}

// ruleid: auth.jwt.algorithm-confusion -- mixing HS with an RSA public key
export function badVerifyMixed(token: string, publicKey: string) {
  return jwt.verify(token, publicKey, { algorithms: ['HS256', 'RS256'] });
}
SAFE
safe.ts
import jwt from 'jsonwebtoken';

const RSA_PUBLIC = process.env.JWT_PUBLIC_KEY!;
const HMAC_SECRET = process.env.JWT_HMAC_SECRET!;

// ok: auth.jwt.algorithm-confusion -- RS256 matches the asymmetric key
export function goodVerifyAsymmetric(token: string) {
  return jwt.verify(token, RSA_PUBLIC, { algorithms: ['RS256'] });
}

// ok: auth.jwt.algorithm-confusion -- HS256 with a real shared secret, not a PEM
export function goodVerifySymmetric(token: string) {
  return jwt.verify(token, HMAC_SECRET, { algorithms: ['HS256'] });
}

Suppressing this rule

If a finding is a genuine false positive, scope the suppression to the exact line and leave a reason, never disable the rule project-wide. Disable directives are line-scoped by design.

// oauthlint-disable-next-line auth.jwt.algorithm-confusion -- <reason>

References

https://datatracker.ietf.org/doc/html/rfc7518#section-3.1 ↗https://owasp.org/www-project-api-security/ ↗