You click:
“Login with Google.”
You authenticate.
Google asks whether you want to continue.
You approve.
Then you’re redirected back to the application.
It looks completely normal.
But behind that simple flow is one of the most security-sensitive pieces of modern web authentication:
redirect_uri
It tells the identity provider:
“After authentication, send the user back here.”
That sounds harmless.
It isn’t.
If an application validates redirect URLs incorrectly, an attacker may be able to manipulate where authentication results are sent.
Depending on the OAuth flow and implementation, that can lead to:
- Authorization-code leakage
- Token exposure
- Account compromise
- Session compromise
- Cross-account linking
- OAuth phishing
- Login CSRF
- Authorization-code interception
And the scary part is that the attacker may never need to break the identity provider.
The identity provider may be functioning perfectly.
The vulnerability can exist entirely in the application’s redirect URI validation.
What Is OAuth?
OAuth is an authorization framework used by applications to obtain access to resources or establish delegated access.
You’ve probably seen:
Continue with GoogleContinue with GitHubContinue with MicrosoftContinue with Apple
A simplified authorization flow looks like:
User ↓Application ↓Identity Provider ↓User authenticates ↓Authorization granted ↓Redirect back to application ↓Application processes authorization result
The critical component is:
redirect_uri
What Is a Redirect URI?
A redirect URI is the destination where the identity provider sends the browser after authorization.
For example:
https://example.com/oauth/callback
The application registers this URI with the identity provider.
The authorization request might conceptually contain:
client_id=12345redirect_uri=https://example.com/oauth/callbackresponse_type=codescope=openid
The identity provider should only redirect to an approved destination.
That restriction is extremely important.
Why Does OAuth Care About the Redirect?
Imagine the identity provider authenticates the user successfully.
It generates:
authorization code
and sends the browser to:
https://example.com/oauth/callback?code=ABC123
The application receives the code and exchanges it for tokens.
The flow is:
Authentication ↓Authorization Code ↓redirect_uri ↓Application ↓Token Exchange
If an attacker can manipulate the redirect destination, the authorization code could potentially be delivered somewhere it shouldn’t be.
The Basic Vulnerability
A vulnerable application might accept:
https://example.com/oauth/callback
but incorrectly allow variations such as:
https://example.com/oauth/callback?next=...
or other attacker-influenced forms.
The problem is usually not:
“The URL contains parameters.”
The problem is:
The application fails to enforce the exact redirect URI security boundary.
Exact Matching Matters
Suppose the legitimate redirect URI is:
https://example.com/oauth/callback
A secure implementation should have a clearly defined policy for exactly which URI is allowed.
Dangerous validation often looks conceptually like:
Starts with:https://example.com
That is not equivalent to:
Exactly:https://example.com/oauth/callback
For security-sensitive redirects, loose string matching can be dangerous.
The Classic “Starts With” Mistake
Imagine a developer writes logic equivalent to:
if redirect_uri starts with:https://example.com allow
An attacker might try to construct a URL whose beginning appears legitimate but whose actual destination is not.
For example, the security question becomes:
Does the application validate the URL's actual origin?
or merely:
Does the string look like it starts with our domain?
Those are very different security models.
Why URL Parsing Is Hard
URLs have many components:
scheme://userinfo@host:port/path?query#fragment
For example:
https://user@example.com:443/login?next=/home
A developer who validates URLs using simple string operations can easily misunderstand which part represents the actual destination.
Security-sensitive URL validation should use a proper URL parser and explicit allowlisting rather than fragile string comparisons.
The Subdomain Problem
Suppose an organization owns:
example.com
and trusts:
login.example.com
A developer may think:
“Anything under example.com is safe.”
But subdomains can have different security properties.
For example:
trusted.example.comold.example.comdev.example.comuser-content.example.com
One compromised or attacker-controlled subdomain could potentially become relevant to an OAuth flow.
This is why OAuth redirect URIs should generally be narrowly scoped.
Wildcard Redirect URIs Are Dangerous
Imagine an application allows:
https://*.example.com/oauth/callback
That seems convenient.
But now every matching subdomain becomes part of the authentication trust boundary.
If one subdomain is:
- abandoned
- vulnerable
- user-controlled
- hosted by a third party
- vulnerable to takeover
the OAuth configuration may inherit that risk.
OAuth and Subdomain Takeover
This is an interesting attack chain:
Forgotten subdomain ↓Subdomain takeover ↓Trusted OAuth redirect ↓Authorization response ↓Potential credential/token exposure
Neither vulnerability needs to be catastrophic alone.
Together, they can become significantly more serious.
This is why security researchers think about vulnerability chaining.
The Open Redirect Connection
Another dangerous combination is:
OAuth+Open Redirect
Suppose:
https://example.com/oauth/callback
is trusted.
But the callback itself redirects users based on an unsafe parameter:
https://example.com/oauth/callback?next=...
If the application can be made to redirect an authorization result toward an attacker-controlled destination, the redirect becomes part of the attack chain.
However:
An open redirect does not automatically mean OAuth token theft.
The exact OAuth implementation determines whether sensitive information can actually be exposed.
Authorization Code Flow
Modern OAuth deployments commonly use an authorization code.
The conceptual flow is:
User ↓Client ↓Authorization Server ↓Login ↓Authorization Code ↓Redirect URI ↓Client ↓Token Endpoint ↓Access Token
The authorization code is generally preferable to sending access tokens directly through the browser.
But the code still needs protection.
PKCE Makes a Huge Difference
PKCE stands for:
Proof Key for Code Exchange
It was designed to protect authorization-code flows against certain interception attacks.
The client creates a secret:
code_verifier
and derives:
code_challenge
The authorization request contains the challenge.
Later, when exchanging the authorization code, the client must provide the verifier.
Conceptually:
Client │ ├── code_challenge ↓Authorization Server │ ├── authorization code ↓Client │ └── code_verifier ↓Authorization Server
If the verifier doesn’t match, the exchange should fail.
Why PKCE Matters
Suppose an attacker somehow obtains:
authorization_code
Without proper protections, they might attempt to redeem it.
With PKCE:
authorization_code+wrong verifier=rejected
This significantly reduces the impact of certain authorization-code interception scenarios.
But PKCE doesn’t fix every redirect URI vulnerability.
PKCE Is Not a Magic Shield
A common misconception is:
“We’re using PKCE, so redirect URI validation doesn’t matter.”
Wrong.
You still need:
- Correct redirect URI validation
- Proper state handling
- Secure token handling
- Correct client authentication where applicable
- Exact authorization-server configuration
PKCE protects one important part of the flow.
It doesn’t make every OAuth implementation secure automatically.
The state Parameter
OAuth clients commonly use:
state
to bind an authorization response to the request that initiated it.
Conceptually:
User starts login ↓Application generates state ↓Authorization request ↓Identity provider ↓Callback + state ↓Application verifies state
If the state doesn’t match, the application should reject the response.
Why State Matters
Without proper state validation, applications can become vulnerable to certain forms of:
- Login CSRF
- Authorization response injection
- Account-linking confusion
The basic security principle is:
The application should know that the OAuth response belongs to the login transaction the user actually initiated.
Login CSRF
Here’s a simplified example.
Imagine an attacker authenticates with their own identity provider account.
They obtain an authorization response.
Then they somehow cause a victim’s browser to process that response.
If the application doesn’t properly bind the authorization response to the victim’s login attempt, the victim could end up logged into the attacker’s account.
That sounds harmless.
It isn’t.
The victim might then upload:
- Personal information
- Private documents
- Payment information
- Messages
- Sensitive account data
thinking they’re using their own account.
The attacker can later access the information stored in the account they control.
This is a powerful example of why OAuth security isn’t only about stealing tokens.
Account Linking Is Another Dangerous Area
Consider an application supporting:
Email/password+Login with Google
A user already has:
alice@example.com
If the application automatically links OAuth identities based solely on email without properly verifying ownership, account-linking vulnerabilities can arise.
The secure approach is to require an explicit, authenticated account-linking flow.
OAuth Redirect URI Validation Checklist
When assessing an OAuth implementation you own or are authorized to test, ask:
Is the redirect URI:
- Exactly registered?
- Strictly matched?
- Scheme validated?
- Host validated?
- Port validated?
- Path validated?
- Normalized consistently?
- Free from unexpected wildcards?
And:
Does the application:
- Use PKCE?
- Validate
state? - Protect authorization codes?
- Avoid tokens in URLs where possible?
- Prevent open redirects?
- Require explicit account linking?
- Validate issuer and audience?
How Bug Bounty Researchers Analyze OAuth
If a bug bounty program explicitly permits OAuth testing, begin by mapping the normal flow.
Capture:
Authorization endpointClient IDRedirect URIStatePKCE parametersScopeResponse type
Don’t immediately attempt to steal tokens.
First understand how the system is supposed to work.
Step 1: Capture a Normal Login
Start with:
Login with Google
or another supported identity provider.
Record the normal authorization request.
You’ll typically see something conceptually like:
client_id=...redirect_uri=...response_type=codescope=...state=...code_challenge=...
Step 2: Identify the Registered Redirect
Determine which callback the application normally uses.
For example:
https://example.com/auth/callback
Now ask:
How strictly is this URI enforced?
Step 3: Look for Loose Validation
During authorized testing, researchers may examine how the application handles controlled variations.
The objective is not to redirect real users.
The objective is to determine whether:
Approved URI
is treated differently from:
Unexpected URI
and whether the identity provider or application rejects the variation.
Step 4: Test With Your Own OAuth Account
Use:
Researcher account+Researcher OAuth account
This allows you to safely verify:
- Login binding
- Redirect behavior
- State handling
- Account linking
- Authorization-code handling
without touching another user’s identity.
Step 5: Prove the Security Boundary
A good OAuth finding should demonstrate something concrete.
For example:
Registered redirect ↓Unexpected redirect accepted ↓Authorization response reaches controlled endpoint ↓Security-sensitive information exposed
Or:
OAuth response ↓State not validated ↓Attacker-controlled authorization response accepted ↓Account-linking/login confusion
Don’t Confuse Reflection With Exploitation
Suppose you modify:
redirect_uri
and the application reflects it somewhere.
That alone does not prove:
“OAuth takeover.”
You need to establish:
Input ↓OAuth behavior ↓Sensitive data or authorization impact
Security reports should focus on the actual impact.
Common OAuth False Positives
Redirect URI rejected
Good.
Authorization server rejects unknown URI
Good.
URL reflected in an error message
Not necessarily vulnerable.
Open redirect exists
Interesting, but not automatically an OAuth vulnerability.
Authorization code appears in browser URL
Normal for code-based OAuth flows.
PKCE is present
Good—but other security controls still matter.
OAuth Authorization Code vs Access Token
These are not the same.
Authorization Code
Temporary credential used to obtain tokens.
Access Token
Credential used to access protected resources.
A secure architecture tries to minimize the exposure of both.
For browser-based applications, the authorization code flow with PKCE is generally preferable to legacy patterns that expose access tokens directly through the front channel.
Never Put Long-Lived Secrets in Front-Channel URLs
URLs can leak through:
- Browser history
- Server logs
- Proxy logs
- Analytics
- Screenshots
- Referrer information
- Monitoring systems
That’s one reason OAuth implementations should carefully control what sensitive information travels through URLs.
OAuth and URL Fragments
Older OAuth flows sometimes placed access tokens in the URL fragment:
https://example.com/callback#access_token=...
Fragments aren’t sent to the server in the normal HTTP request, but browser-side JavaScript can access them.
Modern application design generally favors safer authorization-code-based patterns rather than exposing access tokens directly through the browser.
OAuth and Mobile Apps
Mobile applications introduce additional complexity.
Instead of:
https://example.com/callback
you may encounter:
myapp://callback
or platform-specific universal/app links.
Poorly configured custom URL schemes can introduce interception risks because other applications may attempt to register the same scheme.
Modern mobile OAuth guidance generally favors platform-supported mechanisms such as claimed HTTPS links where appropriate.
OAuth and Desktop Applications
Desktop apps face similar concerns.
A local application may use:
http://127.0.0.1:<port>/callback
for OAuth.
This can be secure when implemented correctly, but the application must carefully handle:
- Random ports
- State
- PKCE
- Local callback validation
- Process lifecycle
Never assume:
“localhost automatically means safe.”
OAuth and Single Sign-On
Enterprise SSO makes redirect security even more important.
Imagine:
Employee ↓Company App ↓Identity Provider ↓SSO ↓Company App
If redirect handling is broken, the impact can potentially extend across multiple applications.
A single identity provider may authenticate users into:
EmailHRCRMCloudDeveloper toolsInternal dashboards
OAuth configuration is therefore part of the organization’s identity security boundary.
How Developers Should Secure OAuth Redirects
1. Use Exact Redirect URIs
Avoid overly broad patterns.
Prefer:
https://example.com/oauth/callback
over:
https://*.example.com/*
when possible.
2. Use PKCE
Especially for public clients and browser/mobile authorization flows.
3. Validate state
Generate unpredictable state values and verify them when the callback returns.
4. Prevent Open Redirects
An OAuth callback should not become a generic redirector.
5. Minimize Redirect Trust
Treat redirect destinations as security-sensitive configuration, not ordinary user input.
6. Avoid Silent Account Merging
Require proper verification before linking OAuth identities to existing accounts.
7. Protect Tokens
Don’t expose access or refresh tokens unnecessarily through:
- URLs
- Logs
- Analytics
- Client-side debugging
OAuth Security Checklist
Redirects
- Exact redirect URI matching
- No unnecessary wildcards
- No arbitrary subdomains
- No user-controlled callback destinations
- No open redirect in callback flow
Authorization
- Use PKCE
- Validate
state - Validate issuer
- Validate audience where applicable
- Validate authorization responses
Tokens
- Short-lived access tokens
- Secure refresh-token storage
- Refresh-token rotation where appropriate
- No sensitive tokens in logs
- No unnecessary token exposure in URLs
Account Linking
- Explicit linking
- Verify account ownership
- Don’t silently merge identities
- Notify users of new linked accounts
A Realistic OAuth Attack Chain
A serious OAuth vulnerability often isn’t one bug.
It may look like:
Loose redirect validation ↓Open redirect ↓Authorization response exposure ↓Authorization-code theft ↓Missing PKCE / weak state handling ↓Code redemption ↓Account compromise
This is why security researchers look at the entire flow, not just individual parameters.
Why OAuth Bugs Are So Valuable in Bug Bounty
OAuth sits directly in the authentication layer.
A small mistake can affect:
- Login
- Account linking
- Identity verification
- SSO
- Third-party access
- Token issuance
That makes OAuth an excellent area for advanced bug-bounty research.
But it also means testing needs to be extremely careful.
Authentication vulnerabilities can affect real users quickly.
What Makes a Strong OAuth Bug Bounty Report?
A good report should contain:
1. Normal Flow
Explain how authentication normally works.
2. Vulnerable Parameter
Identify the specific component:
redirect_uristateclient_idscope
3. Expected Behavior
Explain what should happen.
4. Actual Behavior
Explain what actually happens.
5. Controlled Reproduction
Use your own accounts and infrastructure.
6. Security Impact
Explain exactly what an attacker gains.
7. Remediation
Recommend strict redirect validation and appropriate OAuth protections.
The Bigger Lesson
OAuth has made the internet dramatically more interconnected.
You don’t create a separate password for every service anymore.
You click:
Continue with Google
and suddenly one identity provider is participating in authentication across another application.
That convenience creates trust relationships.
And trust relationships create attack surfaces.
The most important OAuth security question is therefore:
Who is allowed to receive the authentication result?
If the answer is:
“Anyone who can make the URL look legitimate,”
you have a serious problem.
Final Takeaway
OAuth attacks often don’t look like traditional hacking.
There may be:
- No password cracking
- No SQL injection
- No malware
- No brute force
Instead, the attacker finds a tiny mistake in the authentication flow:
redirect URIstatePKCEaccount linkingtoken handling
and turns that mistake into something much bigger.
The key lesson is:
Authentication is only as strong as the weakest redirect and trust relationship around it.
For developers:
Use exact redirect URI matching, PKCE, state validation, secure token handling, and explicit account linking.
For bug bounty hunters:
Don’t stop when you find a suspicious redirect. Trace the entire OAuth flow and prove the actual security impact using accounts and infrastructure you control.
Because the most dangerous part of:
Login with Google
isn’t always the login.
Sometimes it’s:
Where Google sends you after the login.
Discover more from Spyboy blog
Subscribe to get the latest posts sent to your email.
