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
This opens you to CSRF attacks during the OAuth dance. An attacker can trick the victim into logging in as the attacker.
Always generate a cryptographically random state, store it in a session/cookie, and validate it on the callback. PKCE alone is not a substitute for state when handling browser sessions.
VULNERABLE
vulnerable.ts
// ruleid: auth.oauth.no-stateexport const authorizeUrl = 'https://accounts.google.com/o/oauth2/v2/auth?client_id=abc&response_type=code&scope=openid%20email&redirect_uri=https%3A%2F%2Fapp.example.com%2Fcb';export function badRedirect(res: { redirect: (url: string) => void }) { // ruleid: auth.oauth.no-state const params = new URLSearchParams({ client_id: 'abc', response_type: 'code', redirect_uri: 'https://app.example.com/cb', scope: 'openid email', }); res.redirect(`https://accounts.google.com/o/oauth2/v2/auth?${params.toString()}`);}// Per-literal proof: this file mixes a CORRECT authorize URL (carries `state=`)// with a broken one (no `state=`). The old file-level regex saw `state=`// anywhere in the file and suppressed BOTH (a false negative). Only the// state-less literal must flag now.export const correctAuthorize = 'https://accounts.google.com/o/oauth2/v2/auth?client_id=abc&response_type=code&state=xyz789&scope=openid';// ruleid: auth.oauth.no-stateexport const brokenAuthorize = 'https://accounts.google.com/o/oauth2/v2/auth?client_id=abc&response_type=code&scope=openid';
SAFE
safe.ts
import { randomBytes } from 'node:crypto';// ok: auth.oauth.no-stateexport function goodRedirect(res: { redirect: (url: string) => void }) { const state = randomBytes(32).toString('hex'); const params = new URLSearchParams({ client_id: 'abc', response_type: 'code', redirect_uri: 'https://app.example.com/cb', scope: 'openid email', state, }); res.redirect(`https://accounts.google.com/o/oauth2/v2/auth?${params.toString()}`);}
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.