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 →
MEDIUM AI PREVALENCE: HIGH auth.py.jwt.no-algorithms

A JWT is decoded with a verification key but WITHOUT an explicit algorithms allowlist.

Why AI tools produce this: AI coding tools generate this anti-pattern by default, it appears in a large share of AI-written auth code.

Why this matters

Without pinning the accepted algorithms, PyJWT may accept a token signed with an unexpected algorithm, enabling algorithm-confusion attacks (e.g. an RS256 verifier tricked into treating an attacker-supplied HS256 token as valid by using the public key as an HMAC secret).

Always pass an explicit allowlist: jwt.decode(token, key, algorithms=["RS256"]) (or the exact algorithm you expect). List only the algorithms your application actually uses.

VULNERABLE
vulnerable.py
import jwt


def verify_no_algorithms(token: str, key: str):
    # ruleid: auth.py.jwt.no-algorithms
    return jwt.decode(token, key)


def verify_with_audience(token: str, key: str):
    # ruleid: auth.py.jwt.no-algorithms
    return jwt.decode(token, key, audience="my-api")


def verify_with_issuer_and_audience(token: str, key: str):
    # ruleid: auth.py.jwt.no-algorithms
    return jwt.decode(token, key, audience="my-api", issuer="https://issuer.example")


def verify_with_leeway(token: str, key: str):
    # ruleid: auth.py.jwt.no-algorithms
    return jwt.decode(token, key, leeway=10)
SAFE
safe.py
import jwt


# ok: auth.py.jwt.no-algorithms -- explicit algorithms allowlist
def verify_with_algorithms(token: str, key: str):
    return jwt.decode(token, key, algorithms=["RS256"])


# ok: auth.py.jwt.no-algorithms -- allowlist alongside other options
def verify_with_algorithms_and_audience(token: str, key: str):
    return jwt.decode(token, key, algorithms=["RS256"], audience="my-api")


# ok: auth.py.jwt.no-algorithms -- single-arg decode is not a verification (and is covered by no-verify)
def decode_single_arg(token: str):
    return jwt.decode(token)


# ok: auth.py.jwt.no-algorithms -- signature disabled is reported by auth.py.jwt.no-verify, not here
def decode_verify_disabled(token: str, key: str):
    return jwt.decode(token, key, options={"verify_signature": False})


# ok: auth.py.jwt.no-algorithms -- encoding is not affected by the algorithms allowlist rule
def issue_token(claims: dict, key: str):
    return jwt.encode(claims, key, algorithm="RS256")

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.py.jwt.no-algorithms -- <reason>

References

https://pyjwt.readthedocs.io/en/stable/api.html#jwt.decode ↗https://cwe.mitre.org/data/definitions/347.html ↗