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: MEDIUM auth.py.jwt.verify-claims-disabled

PyJWT decode disables audience or issuer checks.

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

Why this matters

jwt.decode(...) is called with an options dict that turns off a claim check: "verify_aud": False, "verify_iss": False, or "verify_nbf": False. Skipping these lets a token minted for a different audience or issuer (for example one from another tenant or a lower-trust service) be accepted here, defeating the boundary those claims are meant to enforce (CWE-347).

Remove the disabling option and validate the claim, e.g. jwt.decode(token, key, algorithms=["RS256"], audience="api", issuer="https://issuer.example.com"). PyJWT only checks aud/iss when you pass the expected value, so supply it rather than disabling the check.

VULNERABLE
vulnerable.py
import jwt

token = "..."
key = "secret"

# ruleid: auth.py.jwt.verify-claims-disabled
claims = jwt.decode(token, key, algorithms=["RS256"], options={"verify_aud": False})

# ruleid: auth.py.jwt.verify-claims-disabled
claims = jwt.decode(token, key, algorithms=["HS256"], options={"verify_iss": False})

# ruleid: auth.py.jwt.verify-claims-disabled
claims = jwt.decode(token, key, algorithms=["RS256"], options={"verify_nbf": False})

from jwt import decode

# ruleid: auth.py.jwt.verify-claims-disabled
claims = decode(token, key, algorithms=["RS256"], options={"verify_aud": False, "verify_iss": False})
SAFE
safe.py
import jwt

token = "..."
key = "secret"

# Audience and issuer are validated by supplying the expected values.
# ok: auth.py.jwt.verify-claims-disabled
claims = jwt.decode(token, key, algorithms=["RS256"], audience="api")

# ok: auth.py.jwt.verify-claims-disabled
claims = jwt.decode(
    token, key, algorithms=["RS256"], audience="api", issuer="https://issuer.example.com"
)

# Disabling signature/expiration is handled by other rules, not this one.
# ok: auth.py.jwt.verify-claims-disabled
claims = jwt.decode(token, key, algorithms=["RS256"], options={"verify_signature": False})

# ok: auth.py.jwt.verify-claims-disabled
claims = jwt.decode(token, key, algorithms=["RS256"], options={"verify_exp": False})

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.verify-claims-disabled -- <reason>

References

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