OAuth 2.0
debt(d7/e7/b7/t7)
Closest to 'only careful code review or runtime testing' (d7). The detection_hints indicate semgrep can catch specific patterns like missing state parameter validation, but automated detection is explicitly marked 'no'. Most OAuth misimplementations (wrong flow choice, insecure token storage, lax redirect_uri matching) require manual code review or security testing to surface — they won't be caught by default tooling and often only manifest under adversarial conditions.
Closest to 'cross-cutting refactor across the codebase' (e7). The quick_fix sounds simple in principle, but remediating a misimplemented OAuth flow (e.g. switching from Implicit to Authorization Code + PKCE, fixing token storage from localStorage to HttpOnly cookies, adding state validation everywhere) typically requires touching the auth initialization, all callback handlers, client-side token storage, and potentially backend session management — spanning multiple files and architectural layers.
Closest to 'strong gravitational pull' (b7). OAuth 2.0 is a foundational auth choice that shapes how every API call, session, and third-party integration is structured. Token lifecycle management, refresh logic, and storage strategy permeate frontend and backend code. Every feature that touches protected resources is shaped by how OAuth was implemented, making it a high-reach, load-bearing choice across the system.
Closest to 'serious trap' (t7). The canonical misconception is explicit and significant: developers routinely treat OAuth 2.0 as an authentication protocol when it is strictly an authorisation framework — a mistake that leads to identity spoofing vulnerabilities. Additionally, the common mistakes list reveals multiple serious gotchas (Implicit flow deprecation, state parameter CSRF, localStorage token storage, redirect_uri matching) that contradict intuitive expectations and differ from how similar-looking patterns work in other auth systems.
Also Known As
TL;DR
Explanation
OAuth 2.0 (RFC 6749) separates authentication from authorisation: the resource owner (user) grants a client (app) access to resources on a resource server via an authorisation server that issues tokens. The main grant types: Authorization Code (web apps — exchanges a code for tokens via back channel), Authorization Code + PKCE (public clients — mobile/SPA where client secret cannot be kept), Client Credentials (machine-to-machine — no user involved), and Device Code (TV/CLI — polls for user approval on a separate device). Access tokens are short-lived (minutes to hours); refresh tokens allow getting new access tokens without re-prompting the user. Scopes define the permissions granted. Common security issues: missing PKCE on public clients enables code interception attacks; missing state parameter enables CSRF on the redirect; open redirect in redirect_uri enables token theft; storing access tokens in localStorage exposes them to XSS. In PHP, use a battle-tested library (league/oauth2-client, thephpleague) rather than implementing flows manually.
Watch Out
Common Misconception
Why It Matters
Common Mistakes
- Using Implicit flow instead of Authorization Code + PKCE for SPAs — Implicit was deprecated in OAuth 2.1 because tokens appear in the URL fragment and browser history.
- Not validating the state parameter on redirect — enables CSRF attacks that bind the attacker's authorisation code to the victim's session.
- Storing access tokens in localStorage — readable by any JavaScript on the page; prefer HttpOnly cookies or in-memory storage.
- Not restricting redirect_uri to an exact match — a wildcard or prefix match enables token theft via open redirects.
Code Examples
// Missing state validation — CSRF on OAuth callback:
public function callback(Request $req): Response {
$code = $req->get('code');
$token = $this->oauth->getAccessToken('authorization_code', [
'code' => $code
]);
// No state check — attacker can initiate flow, trick victim into completing it
Auth::loginWithToken($token);
}
// State validated before token exchange:
public function callback(Request $req): Response {
if ($req->get('state') !== Session::get('oauth_state')) {
throw new InvalidStateException();
}
$code = $req->get('code');
$token = $this->oauth->getAccessToken('authorization_code', [
'code' => $code
]);
Session::forget('oauth_state');
Auth::loginWithToken($token);
}