Key Takeaways
- OAuth 2.0 is an authorization framework, not authentication (use OpenID Connect for identity).
- PKCE (Proof Key for Code Exchange) is mandatory for public clients and SPAs.
- Never store tokens in localStorage—use httpOnly cookies or in-memory storage.
- Always validate redirect_uri strictly to prevent authorization code theft.
OAuth 2.0 powers "Login with Google/Facebook" across millions of applications. But a single misconfiguration can expose user accounts to complete takeover. This guide covers every attack vector.
How OAuth 2.0 Works
OAuth 2.0 defines four main roles: Resource Owner (user), Client (your app), Authorization Server (Google, etc.), and Resource Server (API). The most common flow is the Authorization Code Grant:
- User clicks "Login with Google"
- Browser redirects to Google's authorization endpoint
- User authenticates and consents
- Google redirects back with an authorization code
- Your server exchanges the code for an access token
- Use the token to access user data
Critical Vulnerabilities
1. Authorization Code Interception
If the redirect_uri isn't strictly validated, attackers can steal authorization codes:
// Vulnerable: Open redirect allows code theft
https://auth.example.com/authorize?
client_id=app123&
redirect_uri=https://evil.com/callback& // Attacker's domain!
response_type=code&
scope=read:profile
Attacker crafts a malicious link. When victim clicks it, the authorization code is sent to the attacker's server. The attacker exchanges it for an access token → full account access.
2. PKCE Bypass
PKCE (Proof Key for Code Exchange) prevents code interception by requiring a secret verifier. But some implementations allow downgrade attacks:
// Server should REJECT if code_challenge was sent but code_verifier is missing
POST /token
code=abc123&
redirect_uri=https://app.com/callback
// Missing code_verifier! Server should error.
3. Implicit Flow Token Theft
The implicit flow returns tokens directly in the URL fragment, making them vulnerable to:
- Referrer Leakage: Tokens visible in browser history and referrer headers
- XSS Attacks: JavaScript can read the URL fragment
- Browser Extensions: Malicious extensions can capture tokens
Fix: Use Authorization Code + PKCE
The implicit flow is deprecated. Always use the Authorization Code flow with PKCE, even for SPAs. Tokens should never appear in URLs.
4. State Parameter Missing
Without the state parameter, attackers can perform CSRF attacks to link their account to another user's session:
// Always include state and validate it!
const state = crypto.randomUUID();
sessionStorage.setItem('oauth_state', state);
const authUrl = `https://auth.example.com/authorize?
client_id=app123&
redirect_uri=https://app.com/callback&
state=${state}& // Random, unpredictable value
response_type=code`;
5. Token Storage Vulnerabilities
| Storage Method | XSS Risk | CSRF Risk | Recommendation |
|---|---|---|---|
| localStorage | High | None | ❌ Avoid |
| sessionStorage | High | None | ⚠️ Better |
| HttpOnly Cookie | None | Medium | ✅ Best |
| In-Memory (Variable) | Medium | None | ✅ Good for SPAs |
Secure Implementation Checklist
Do
- Use PKCE for all clients
- Validate redirect_uri exactly
- Implement state parameter
- Use short-lived access tokens
- Rotate refresh tokens
Don't
- Use implicit flow
- Store tokens in localStorage
- Accept partial redirect_uri matches
- Skip token validation
- Expose client_secret in frontend
Frequently Asked Questions
Secure your authentication layer.
Read JWT Security Guide