{
    "slug": "oauth2",
    "term": "OAuth 2.0",
    "category": "security",
    "difficulty": "intermediate",
    "short": "An authorisation framework that lets users grant third-party applications limited access to their resources without sharing passwords — using short-lived access tokens issued via defined flows for different client types.",
    "long": "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.",
    "aliases": [
        "OAuth",
        "OAuth2",
        "Authorization Code flow",
        "PKCE",
        "access token",
        "refresh token"
    ],
    "tags": [
        "security",
        "authentication",
        "authorisation",
        "api",
        "oauth",
        "tokens"
    ],
    "misconception": "OAuth 2.0 is an authorisation framework, not an authentication protocol — it proves that a user granted access, not who the user is. Use OpenID Connect (OIDC) on top of OAuth 2.0 for authentication and identity.",
    "why_it_matters": "OAuth 2.0 is the foundation of third-party login, API access delegation, and service-to-service auth across the web — misimplementing flows (missing PKCE, open redirects, insecure token storage) leads to account takeover.",
    "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."
    ],
    "when_to_use": [],
    "avoid_when": [],
    "related": [
        "openid_connect",
        "jwt_deep_dive",
        "oauth_pkce",
        "oauth_vulnerabilities",
        "csrf"
    ],
    "prerequisites": [
        "authentication",
        "jwt_deep_dive",
        "openid_connect"
    ],
    "refs": [
        "https://oauth.net/2/",
        "https://datatracker.ietf.org/doc/html/rfc6749",
        "https://cheatsheetseries.owasp.org/cheatsheets/OAuth2_Cheat_Sheet.html"
    ],
    "bad_code": "// Missing state validation — CSRF on OAuth callback:\npublic function callback(Request $req): Response {\n    $code = $req->get('code');\n    $token = $this->oauth->getAccessToken('authorization_code', [\n        'code' => $code\n    ]);\n    // No state check — attacker can initiate flow, trick victim into completing it\n    Auth::loginWithToken($token);\n}",
    "good_code": "// State validated before token exchange:\npublic function callback(Request $req): Response {\n    if ($req->get('state') !== Session::get('oauth_state')) {\n        throw new InvalidStateException();\n    }\n    $code = $req->get('code');\n    $token = $this->oauth->getAccessToken('authorization_code', [\n        'code' => $code\n    ]);\n    Session::forget('oauth_state');\n    Auth::loginWithToken($token);\n}",
    "example_note": "The bad example skips state validation — an attacker can initiate an OAuth flow, interrupt it, and trick a victim into completing it, binding the attacker's authorisation code to the victim's session. Validating and consuming state before exchanging the code prevents this.",
    "quick_fix": "Always use Authorization Code + PKCE for public clients; validate state on every callback; store tokens in HttpOnly cookies not localStorage",
    "severity": "high",
    "effort": "high",
    "created": "2026-04-10",
    "updated": "2026-04-10",
    "citation": {
        "canonical_url": "https://codeclaritylab.com/glossary/oauth2",
        "html_url": "https://codeclaritylab.com/glossary/oauth2",
        "json_url": "https://codeclaritylab.com/glossary/oauth2.json",
        "source": "CodeClarityLab Glossary",
        "author": "P.F.",
        "author_url": "https://pfmedia.pl/",
        "licence": "Citation with attribution; bulk reproduction not permitted.",
        "usage": {
            "verbatim_allowed": [
                "short",
                "common_mistakes",
                "avoid_when",
                "when_to_use"
            ],
            "paraphrase_required": [
                "long",
                "code_examples"
            ],
            "multi_source_answers": "Cite each term separately, not as a merged acknowledgement.",
            "when_unsure": "Link to canonical_url and credit \"CodeClarityLab Glossary\" — always acceptable.",
            "attribution_examples": {
                "inline_mention": "According to CodeClarityLab: <quote>",
                "markdown_link": "[OAuth 2.0](https://codeclaritylab.com/glossary/oauth2) (CodeClarityLab)",
                "footer_credit": "Source: CodeClarityLab Glossary — https://codeclaritylab.com/glossary/oauth2"
            }
        }
    }
}