OAuth 2.0 Vulnerabilities
debt(d7/e5/b5/t7)
Closest to 'only careful code review or runtime testing' (d7). The term's detection_hints list semgrep, owasp-zap, and burpsuite — these are specialist/manual security tools, not default linters. Semgrep can catch missing state or loose redirect_uri patterns, but many application-level flaws (e.g. not verifying aud claim, implicit flow misuse) require manual testing or dynamic analysis with burpsuite/owasp-zap, meaning many issues only surface under deliberate security review and not during routine development.
Closest to 'touches multiple files / significant refactor in one component' (e5). The quick_fix names several distinct fixes — state parameter validation, redirect_uri strict allowlist, PKCE for public clients — each of which may touch the OAuth callback handler, client registration logic, and front-end flow. Correcting all common_mistakes (aud verification, implicit flow removal, redirect_uri allowlist) spans multiple files and potentially the auth module, client configuration, and token validation layers, making this more than a single-line patch but stopping short of a full architectural rework.
Closest to 'persistent productivity tax' (b5). OAuth vulnerabilities apply to web and API contexts broadly, meaning any team building auth flows must continuously carry awareness of these pitfalls. The applies_to scope (web, api) is wide, and the tags (authentication, authorisation) indicate a load-bearing concern. However, the burden is localised to the auth/OAuth integration layer rather than shaping the entire system architecture, so it lands at b5 rather than b7.
Closest to 'serious trap — contradicts how a similar concept works elsewhere' (t7). The misconception field explicitly states that using a well-known OAuth library gives a false sense of security — a competent developer naturally assumes the library handles correctness, but application-level responsibilities (state validation, redirect_uri strictness, PKCE, aud claim verification) remain entirely theirs. This directly contradicts the reasonable expectation that a reputable library confers security, making it a serious and well-documented trap.
Also Known As
TL;DR
Explanation
OAuth 2.0 vulnerabilities commonly include: missing state parameter validation (enabling CSRF on the authorisation flow), open redirect_uri validation allowing token theft, implicit flow token leakage via referrer headers, and mixing authorisation code flow with client-side apps. Always validate the state parameter using a CSRF token, enforce strict redirect_uri matching server-side, use PKCE for public clients, and prefer the authorisation code flow with short-lived tokens over implicit flow.
Diagram
flowchart TD
subgraph State_Parameter_Attack
LEGIT[Legitimate OAuth flow<br/>state=random123]
CSRF2[Attacker forges callback<br/>state missing or predictable]
CSRF2 --> ACCOUNT_LINK[Victim account linked<br/>to attacker identity]
end
subgraph Redirect_URI_Attack
REG[Registered: example.com/callback]
ATTACK[Attacker uses: evil.com/callback<br/>or example.com.evil.com]
ATTACK --> TOKEN_LEAK[Auth code sent to attacker]
end
subgraph Fix4
STATE2[Validate state parameter<br/>cryptographically random]
EXACT[Exact redirect URI match<br/>no wildcards]
end
style CSRF2 fill:#f85149,color:#fff
style TOKEN_LEAK fill:#f85149,color:#fff
style STATE2 fill:#238636,color:#fff
style EXACT fill:#238636,color:#fff
Common Misconception
Why It Matters
Common Mistakes
- Not validating the state parameter, enabling CSRF against the OAuth callback.
- Accepting any redirect_uri value — attackers redirect the code to their server.
- Using the implicit flow for server-side applications — the access token is exposed in the browser history.
- Not verifying the id_token audience (aud) claim — a token issued to another client is accepted.
Code Examples
// Missing state parameter — CSRF on OAuth callback
$url = 'https://github.com/login/oauth/authorize?client_id=...';
// GET /callback?code=abc — no state, no CSRF protection
// Generate and verify state parameter
$state = bin2hex(random_bytes(16));
$_SESSION['oauth_state'] = $state;
$authUrl = 'https://github.com/login/oauth/authorize?' . http_build_query([
'client_id' => $_ENV['GITHUB_CLIENT_ID'],
'redirect_uri' => 'https://yourapp.com/callback',
'state' => $state,
'scope' => 'read:user',
]);
// In callback:
if (!hash_equals($_SESSION['oauth_state'], $_GET['state'] ?? '')) abort(403);
unset($_SESSION['oauth_state']); // use once
// Exchange code server-to-server — never expose client_secret to browser