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
Sending state on the authorize call is half of the CSRF mitigation; verifying it on the callback is the other half.
Compare the received state to the value you stored before redirecting (session, signed cookie, or Redis). Reject the callback if it's missing or doesn't match.
VULNERABLE
vulnerable.ts
interface Req { query: { state?: string; code?: string }; body: { state?: string };}export function badCallback(req: Req) { // ruleid: auth.oauth.no-state-validation const state = req.query.state; console.log(`received state ${state}`); return req.query.code;}export function badCallback2(req: Req) { // ruleid: auth.oauth.no-state-validation return req.body.state;}
SAFE
safe.ts
interface Req { query: { state?: string }; session: { oauth_state?: string };}declare function verifyState(s?: string): boolean;// ok: auth.oauth.no-state-validationexport function goodCallback(req: Req) { if (req.session.oauth_state !== req.query.state) { throw new Error('CSRF: state mismatch'); } return true;}// ok: auth.oauth.no-state-validation -- reversed operand order is still validationexport function goodCallbackReversed(req: Req) { if (req.query.state !== req.session.oauth_state) { throw new Error('CSRF: state mismatch'); } return true;}// ok: auth.oauth.no-state-validation -- validated via a helperexport function goodCallbackHelper(req: Req) { if (!verifyState(req.query.state)) { throw new Error('CSRF: state mismatch'); } return true;}// ok: auth.oauth.no-state-validation -- state captured into a local, then validatedexport function goodCallbackViaLocal(req: Req) { const state = req.query.state; if (req.session.oauth_state !== state) { throw new Error('CSRF: state mismatch'); } return true;}// ok: auth.oauth.no-state-validation -- searchParams read into a local, then validatedexport function goodCallbackSearchParams(url: URL, stored?: string) { const state = url.searchParams.get('state'); if (!stored || stored !== state) { throw new Error('CSRF: state mismatch'); } return true;}
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.