
Imagine clicking:
Continue with Google
You expect one thing to happen: Google confirms who you are, and the website lets you in. Simple, right?
Behind that innocent-looking button, however, is a chain of redirects, authorization codes, tokens, browser sessions, identity providers, and application logic.
And if even one part of that chain is implemented incorrectly, an attacker may be able to manipulate the flow, steal an authorization code, hijack an account, or make an application associate the wrong identity with the wrong user.
That’s what makes OAuth security interesting.
The attacker doesn’t necessarily need to break Google’s security. They may only need to find one weak assumption in the application using Google.
OAuth itself may be secure. The implementation around it might not be.
This article explores the most important OAuth attack surfaces, common misconfigurations, real-world research, how penetration testers approach OAuth testing, and how developers can prevent these attacks.
1. First: What Exactly Is OAuth?
Before we look at OAuth attacks, let’s first understand what OAuth actually does.
OAuth 2.0 is mainly an authorization framework. It allows an application to access certain resources on your behalf without ever needing your password.
For example, suppose you use a photo application that wants access to your Google Photos. Instead of giving the photo application your Google password, you are redirected to Google. You log in directly there and decide whether you want to give the application access to your photos.
A simplified OAuth authorization-code flow looks like this:
1. "Login with Google"
User ─────────────────────► Application
|
| 2. Authorization request
↓
Authorization Server
|
| 3. User authenticates
| 4. User grants permission
↓
Authorization Code
|
| 5. Code returned
↓
Application
|
| 6. Exchange code
↓
Token Endpoint
|
↓
Access Token
|
↓
API
The key point here is simple: the application never gets your Google password.
Instead, Google sends the application an authorization result. The application can then exchange that result for a token, which it can use to access the resources you approved.
And this is where things start getting interesting. Because every step in this process has to work correctly. If the application makes a mistake somewhere along the way, that mistake can potentially become a security problem.
2. Why Is OAuth Such an Attractive Attack Surface?
OAuth looks simple from the user’s perspective.
Click Login
↓
Authenticate
↓
You're logged in
But behind that simple button, the application has to handle quite a few things:
- authorization requests
- redirect URIs
- authorization codes
- browser sessions
state- PKCE
- tokens
- identity providers
- account linking
- scopes
- callbacks
- identity claims
That’s a lot of moving parts for something that looks like a single “Continue with Google” button.
Think of OAuth as a chain:
Authorization
↓
Redirect
↓
Authorization Code
↓
Callback
↓
Token Exchange
↓
Identity
↓
Application Session
Each step has a specific job. The problem is that a small mistake in any of these steps can sometimes have a much bigger impact.
For example, an application might accept a redirect it shouldn’t, fail to properly validate a state value, mishandle an authorization code, or incorrectly link an OAuth account to an existing user.
And that’s what makes OAuth interesting from a security perspective. The OAuth provider might be doing everything correctly. The weakness could simply be in how the application uses OAuth.
So when testing an OAuth implementation, the goal isn’t necessarily to “break Google” or another identity provider. The real question is:
“Can I make the application trust something it shouldn’t?”
That’s where the fun begins.
3. The OAuth 2.0 Attack Surface
When testing OAuth, don’t just search for /oauth and call it a day. Think about the entire authentication journey.
A typical OAuth flow can involve several endpoints, parameters, redirects, and account-management features. Each one gives you something different to look at.
Authorization Endpoint
You may come across a request like:
GET /authorize?
client_id=12345
&redirect_uri=https://app.example.com/oauth/callback
&response_type=code
&scope=openid profile email
&state=RANDOM_VALUE
At first glance, it might just look like a bunch of parameters. But during testing, some of them deserve extra attention:
client_id
redirect_uri
response_type
scope
state
nonce
code_challenge
code_challenge_method
For example, redirect_uri controls where the authorization response goes, while state helps tie the OAuth flow back to the user’s session.
You don’t need to understand every parameter at once. The important thing is to identify what the application is sending and how it validates the response.
Callback
After authentication, the provider may redirect the browser back to the application:
/oauth/callback?code=ABC123&state=XYZ789
This endpoint is particularly interesting during testing because this is where the application receives the result of the OAuth login.
Questions start popping up:
- Does it validate the
state? - What happens if the
codeis changed? - Can the callback be accessed directly?
- Does the application correctly associate the response with the user who started the flow?
Small mistakes here can turn into serious authentication issues.
Token Endpoint
The application may then exchange the authorization code at an endpoint such as:
/oauth/token
This is where the authorization code is turned into tokens that can be used to access protected resources.
From a testing perspective, you’re interested in whether the application is handling this exchange securely and whether the tokens are being used as intended.
Discovery Endpoint
If OpenID Connect is involved, you may also find:
/.well-known/openid-configuration
This endpoint can provide useful information about the identity provider, including authorization and token endpoints, supported flows, scopes, and other capabilities. For a pentester, it’s basically a nice map of the OAuth/OIDC setup.
Account Linking
And then there are features such as:
Connect Google
Connect GitHub
Connect Microsoft
Link social account
These deserve special attention. The application now has to answer a very important question:
Which external identity belongs to which internal account?
If that relationship is handled incorrectly, an attacker may be able to link their own OAuth identity to someone else’s account or otherwise interfere with the login process.
So when testing OAuth, don’t stop at the login button. Follow the entire journey. The authorization request, redirects, callback, token exchange, and account-linking functionality can all tell you something about how the application actually implements OAuth.
4. Five Core OAuth Vulnerabilities
1: Weak redirect_uri Validation (Open Redirects & Path Traversal)
The Vulnerability:
When you log in through OAuth, the service needs to know where to send you back after you authenticate. It uses the redirect_uri parameter to handle this return trip. The vulnerability occurs when the authorization server relies on loose matching rules instead of strictly checking the full destination address against a trusted list.
Common misconfigurations include:
- Wildcard matching: Allowing any subdomain (e.g.,
*.example.com), which becomes dangerous if an attacker controls a sub-domain or finds a cross-site scripting (XSS) vulnerability on one. - Path traversal tricks: Accepting directory manipulation tricks (like
/oauth/callback/../../attacker) to force the browser out of the safe directory. - Flawed regex patterns: Forgetting to anchor web domains properly, making lookalike URLs (like
example.com.attacker.com) pass as legitimate.
How the Attack Works:
- The Setup: The attacker builds a custom link to start the login process, but swaps the normal return address with a site they control.
- The Bait: The attacker tricks you into clicking this link to log in.
- The Intercept: Once you successfully sign in, the login provider redirects your browser to the attacker’s server—passing your secret authorization code or access token directly in the URL bar.
The Impact:
The attacker reads the authorization code straight from their own server logs, exchanges it with the provider, and logs in directly as you—achieving a full Account Takeover (ATO).
Mitigation
- Strict Whitelisting: Enforce exact, full-URL matching for all return addresses instead of relying on pattern matching or subdomains.
- Avoid Wildcards: Reject loose rules like
*.domain.comto prevent compromised sub-domains from putting your primary app at risk. - Deploy PKCE: Implement Proof Key for Code Exchange (PKCE) alongside strict URI checks so authorization codes cannot be traded for access tokens even if intercepted.
2: Cross-Site Request Forgery (CSRF) via Missing or Unvalidated state
The Vulnerability:
When a site lets you “Log in with Google” or another provider, it needs a way to guarantee that the user completing the login is the exact same person who started it. The state parameter acts as a secret safety token bound to your browser session to ensure this. The vulnerability occurs when a website omits this state token entirely or forgets to check if it matches your active session.
How the Attack Works:
- The Setup: The attacker begins logging into the app using their own credentials, but interrupts the process right before completion to steal the valid login code generated for them.
- The Bait: The attacker embeds this code into a link and tricks you into clicking it while you are logged into the app.
- The Switch: Because the app isn’t checking a unique
statetoken, it processes the attacker’s login code inside your browser session without second-guessing it.
The Impact:
The app unknowingly links your personal account to the attacker’s third-party login. From that point on, the attacker can sign in using their own credentials whenever they want and gain full access to your private profile and data.
Mitigation
- Mandatory Tokens: Always generate a unique, random
statetoken for every single login request. - Session Verification: Store the token in a secure,
HttpOnlycookie or session, and strictly confirm it matches when the provider returns the response. - One-Time Use: Instantly expire and destroy the
statetoken as soon as it is validated.
3: Pre-Account Takeover via Unverified Email Trust
The Vulnerability:
Many applications allow users to register using either a traditional email/password combination or a social login like “Sign in with Google.” The vulnerability occurs when an application automatically links an incoming OAuth login to an existing account based solely on a matching email address—without verifying whether the OAuth provider actually validated that the user owns that email address.
If an Identity Provider (IdP) doesn’t require email verification or allows unverified sign-ups, an attacker can create an OAuth account using any victim’s email address.
How the Attack Works:
- The Trap: The attacker registers a standard email/password account on the target app using the victim’s email address (
victim@example.com). - The Normal User: Months later, the actual owner of
victim@example.comvisits the site and chooses the faster “Sign in with Google” option. - The Merge: The application sees that
victim@example.comalready exists in its database and automatically links the Google OAuth profile to the attacker’s original email/password account—assuming they are the same person.
The Impact:
The real user populates the account with private data, credit cards, and personal details. Meanwhile, the attacker still knows the original password they set during step one, allowing them to sign in alongside the victim whenever they want for a complete Account Takeover (ATO).
Mitigation
- Check Verification Claims: Always inspect and validate explicit identity claims (like
email_verified: truein OpenID Connect ID tokens) before trusting an email address. - Require Explicit Linking: Never merge standard password accounts with third-party OAuth accounts automatically. Always force the user to authenticate using their existing password before linking a new provider.
- Disable Password Auth Post-Link: If an account is safely linked, ensure authentication changes require multi-factor verification or confirmation via email.
4: Authorization Code Replay / Lack of PKCE Enforcement
The Vulnerability:
When an application completes an OAuth flow, the identity provider hands back a temporary authorization code. The client app is supposed to trade this code for a real access token right away. The vulnerability occurs when authorization codes can be reused multiple times or when public clients—like mobile apps or Single Page Applications (SPAs)—do not enforce PKCE (Proof Key for Code Exchange) to tie code requests to the original requester.
Because public apps store their code right in the browser or device, they cannot keep secret credentials safe on a private server, making code interception a constant threat.
How the Attack Works:
- The Snag: The attacker intercepts a legitimate user’s temporary authorization code through referrer headers, browser history, open network logs, or malicious apps registered on the same mobile device.
- The Replay: If the authorization server fails to expire the code immediately after its first use, or fails to require a dynamic PKCE verification check (
code_verifier), the attacker submits the intercepted code to the token endpoint themselves. - The Access: The authorization server blindly exchanges the intercepted code for a fresh access token, treating the attacker as the original user.
The Impact:
The attacker bypasses client authentication completely, acquires a valid access token, and gains unauthorized access to the victim’s account session and private data.
Mitigation
- Enforce Strict PKCE: Make Proof Key for Code Exchange (PKCE) mandatory for all OAuth flows—especially for public clients like single-page web apps and native mobile applications.
- One-Time Use Enforcement: Ensure authorization codes expire instantly after being redeemed once.
- Automatic Revocation on Reuse: If a code that was already used is presented a second time, immediately revoke all tokens previously issued from that authorization code.
5: Scope Escalation & Consent Phishing
The Vulnerability:
OAuth applications ask for specific permissions (called “scopes”) to define what user data they can access. The vulnerability occurs when applications request unnecessarily broad access—like full read/write permissions instead of basic profile access—or when identity providers allow users to manually edit authorization URLs to request high-privilege administrative scopes without additional approval controls.
Because users are accustomed to clicking “Allow” on permission prompts, attackers exploit this trust to bypass passwords and multi-factor authentication (MFA) entirely.
How the Attack Works:
- The Bait: An attacker registers a legitimate-looking third-party OAuth application (e.g., “Invoice Generator”) and sends target users a direct link to sign in.
- The Request: The application requests highly invasive permissions, such as full access to emails, cloud drives, or organization-wide administration settings.
- The Consent: The user approves the prompt, trusting the familiar login interface of their identity provider without realizing how much access they are handing over.
The Impact:
The attacker receives an access token that gives them persistent, direct API access to the victim’s emails, files, and corporate infrastructure. No passwords are stolen, and password resets won’t kick the attacker out—resulting in full tenant compromise.
Mitigation
- Follow Principle of Least Privilege: Request only the absolute minimum scopes required for your application to function.
- Require Admin Consent Workflows: Block standard users from granting dangerous or tenant-wide scopes without explicitly routing the request through an IT administrator.
- Enforce Publisher Verification: Require third-party OAuth apps to complete identity verification before they can request elevated permissions from users.
5. Mitigation Strategies for Developers
To secure OAuth 2.0 implementations across the entire authorization chain, development and security teams should apply these core technical controls:
- Enforce Exact String Matching for
redirect_uri: Require full-path, exact string comparisons on every redirect URI. Completely disallow wildcards (*), regex pattern matching, subdomains, and relative paths. - Mandate Cryptographically Secure
stateTokens: Generate unique, high-entropystateparameters bound directly to the user’s secure session cookie. Validate these tokens strictly on the callback and destroy them immediately after a single check. - Enforce PKCE (RFC 7636) Across All Flows: Require Proof Key for Code Exchange (PKCE) for every authorization code request—especially for public clients like single-page apps (SPAs) and mobile applications. Always enforce
S256(SHA-256) as the code challenge method rather thanplain. - Verify Identity Claims Before Account Linking: Never merge existing accounts based solely on a matching email address. Verify the identity provider’s cryptographic signature and explicitly confirm the presence of
email_verified: truein the ID token. - Enforce Short-Lived, One-Time Authorization Codes: Set authorization code lifetimes to expire rapidly (60 seconds or less). Implement automatic token revocation if an authorization code is ever presented a second time.
Summary Table
| Vulnerability | Key Security Control | Standard / RFC |
|---|---|---|
#1 Flawed redirect_uri | Strict exact-string whitelisting | OAuth 2.1 / RFC 6749 |
#2 Missing state Token | Session-bound Anti-CSRF parameters | OAuth 2.0 Threat Model (RFC 6819) |
| #3 Unverified Email Trust | Explicit email_verified validation | OpenID Connect Core 1.0 |
| #4 Code Replay / Interception | Mandatory PKCE (S256) + rapid expiration | RFC 7636 |
| #5 Scope Escalation | Principle of least privilege & admin consent | OAuth 2.0 Security Best Current Practice |
6. Real-World Case Study: Booking.com Account Takeover via Open Redirect Chain
The Vulnerability:
In a major real-world bug bounty disclosure, security researchers from Salt Labs uncovered a critical OAuth chain on Booking.com. The core issue combined weak redirect_uri validation with an open redirect vulnerability hosted on an accepted subdomain. Because the main OAuth authorization server only checked if the destination belonged to the trusted base domain, it blindly trusted subdomains containing open redirect flaws.
How the Attack Worked:
- The Setup: The attacker discovered an open redirect on an authorized subdomain. They crafted a specialized “Sign in with Facebook/Google” link for Booking.com, setting the
redirect_urito point to that vulnerable subdomain while chaining it to pass parameters to the attacker’s server. - The Bait: The attacker tricked a victim into clicking the crafted login link.
- The Chain: After the victim successfully authenticated, Booking.com’s OAuth server validated that the
redirect_uristarted with a trusted subdomain and approved the request. The open redirect immediately triggered, bouncing the victim’s browser to the attacker’s external server while handing over the OAuth authorization token in the URL.
The Impact:
The attacker extracted the authorization code from their server logs, exchanged it for an active user session, and achieved full Account Takeover (ATO). This gave them direct access to the victim’s full profile, stored payment cards, personal travel details, and live reservation histories—all without needing the user’s password.
Lessons from the Salt Labs Disclosure
- Subdomain Trust is a Fallacy: Never assume a subdomain is secure just because your organization owns it. A single open redirect or XSS on a secondary domain compromises the main OAuth client.
- Chain Vulnerability Testing: Security audits should evaluate how OAuth endpoints react when combined with open redirects, header injection, or path traversal vectors.
- Strict URI Matching: Complete exact-string whitelisting prevents open redirect chains by rejecting any
redirect_urithat appends dynamic redirect chains.
Conclusion
OAuth 2.0 has simplified authentication across the web, but its reliance on complex client-side redirects, parameters, and token exchanges means that implementation errors carry severe consequences. Identity providers like Google, Microsoft, and GitHub work hard to keep their core infrastructure secure, but security ultimately breaks down at the application layer when developers misconfigure callbacks, drop session validation, or blindly trust incoming tokens.
For security testers, auditing OAuth requires analyzing the entire authorization lifecycle rather than relying on automated scanners. For developers, adhering to strict validation protocols—like exact URI matching, mandatory PKCE, and session-bound state enforcement—remains the primary line of defense against account takeover.