OAuth 2.0 Attack Surface
OAuth 2.0 defines how a resource owner can grant a third-party application limited access to a service without sharing credentials. It is not an authentication protocol. OpenID Connect (OIDC) is a layer on top of OAuth 2.0 that adds authentication. Using OAuth 2.0 alone to authenticate users is a documented class of implementation error. The attack surface spans redirect URI validation, state parameter handling, token storage, and scope enforcement.
Redirect URI Manipulation
In the authorization code flow, the authorization server redirects the user back to a URI registered by the client, appending the authorization code as a query parameter. If the authorization server validates the redirect URI too loosely, an attacker can manipulate the URI to redirect the code to a server they control.
Loose validation patterns that are exploitable:
- Prefix matching:
https://example.comis registered; the server acceptshttps://example.com.attacker.com. - Substring matching: any URI containing
example.comis accepted. - Path traversal:
https://example.com/callbackis registered; the server acceptshttps://example.com/callback/../attacker-path. - Parameter injection:
https://example.com/callback?foo=baris registered; the server allows additional parameters that redirect elsewhere.
The authorization code delivered to the attacker's URI can be exchanged for an access token at the token endpoint, granting access to the victim's account.
Missing State Parameter
The state parameter is a random value generated by the client, included in the authorization request, and echoed back by the authorization server in the redirect. The client verifies the returned state matches what it sent. This prevents CSRF against the OAuth callback: without state validation, an attacker can initiate an authorization flow and trick a victim into completing it, binding the attacker's authorization code to the victim's session.
The state parameter must be unguessable, tied to the user's session, verified exactly once, and discarded after use. A static or predictable state value provides no CSRF protection. PKCE (Proof Key for Code Exchange) provides an alternative binding mechanism using a cryptographic challenge, but it addresses a different threat (authorization code interception by a malicious app on the same device) and does not replace state for CSRF protection in web applications.
Authorization Code Interception and PKCE
PKCE was designed for public clients (mobile and single-page apps) that cannot store a client secret. Without PKCE, a malicious application on the same device could register the same custom URI scheme and intercept the authorization code from the redirect. PKCE binds the authorization request to the token exchange through a code verifier and code challenge: the client sends a hash of the verifier in the authorization request and the verifier itself in the token request. An attacker who intercepts the code cannot exchange it without the verifier.
Token Leakage via Referrer and Logs
Access tokens passed as URL query parameters appear in server access logs, browser history, and HTTP Referer headers. The implicit flow, now deprecated in OAuth 2.1, returned tokens directly in the URL fragment. Applications using this flow exposed tokens to any JavaScript on the page and to analytics or logging endpoints that captured full URLs. Access tokens must be transmitted in HTTP headers, not in URLs.