OAuth 2.0 Misconfiguration in SaaS Authorization Flows
Misconfigurations in OAuth 2.0 follow predictable patterns security teams can learn to spot.

OAuth 2.0 misconfigurations follow a specific, repeatable pattern, and each one leaves a trail a technical team can learn to spot before someone else finds it first. The protocol was built to grant limited access to a resource on a user's behalf. It was never built to answer the question "who is this person?" That distinction sounds small. It isn't, and most of the mistakes below trace straight back to a team forgetting it.
OpenID Connect exists because OAuth alone can't verify identity. It sits on top of OAuth to handle authentication. But plenty of SaaS teams skip that layer and just use OAuth by itself, treating a successful token exchange as proof of who a user is, rather than what it actually proves: what that user is allowed to do. Get that backwards, and you skip an identity check the protocol was never designed to give you.
Part of the blame sits with the spec itself. OAuth 2.0 leaves a lot open: many parameters are optional, grant types carry different security tradeoffs, and a lot of the hard calls get pushed onto whoever's building the client. That openness is why thousands of SaaS products can plug into each other, and it's also why the attack surface is so wide. Every optional parameter is a spot where a developer can guess wrong, and plenty do.
In January 2025, the IETF published RFC 9700, the first update to OAuth security guidance since 2013. The vulnerability classes documented by security researchers map to attacks that have already worked against real targets. These are lessons built from incidents, not theory.
Salesloft-Drift breach as an illustration of OAuth misconfigurations at scale
In March 2025, attackers got into Salesloft's GitHub environment, then used that foothold to grab OAuth tokens tied to the Drift integration, reaching Salesforce instances at more than 700 organizations, Obsidian Security reported.
What made the damage spread that far wasn't some exotic exploit. It was refresh tokens that never expired. The initial breach gave attackers token access that lasted for months before anyone noticed, and once inside, they ran ten days of active data theft before anyone caught it.
SSO and MFA did nothing to stop this, and that should unsettle any security team leaning on those controls as a safety net. OAuth tokens work independently of both. Once someone holds a valid token, they have access, full stop, no matter how carefully the original login was protected.
The Allianz Life incident follows the same script. A Salesforce compromise there exposed 1.1 million customer records, Obsidian Security reported.
Neither of these was a zero-day. Neither involved some exotic nation-state exploit chain. What connects them is a gap between how OAuth is supposed to behave and how organizations actually configure it: default settings nobody revisited, token lifetimes nobody bothered to cap. Every section below is a specific flavor of that same gap.
How authorization codes get delivered to an attacker through redirect URI validation failures
The redirect URI tells the authorization server where to send the user, and the authorization code, once login finishes. Get this wrong, and it's widely documented as one of the most dangerous misconfigurations in OAuth deployments today. Most teams still treat it as a minor detail, and that's exactly the mistake Doyensec's research points to.
The basic attack works like this: an attacker crafts an authorization request that swaps in their own domain instead of the legitimate redirect URI. If the server checks that URI with wildcard matching or path-prefix matching instead of a strict, exact-match comparison, the fake URI slides right through. The authorization code lands on the attacker's server. From there, they trade it for an access token, and the account is theirs.
A second, sneakier version appears constantly in bug bounty writeups: chaining with an open redirect. The attacker finds an open redirect flaw somewhere on the legitimate app's domain, then builds a redirect URI that technically clears the allowlist check (it really is on the correct domain) but bounces the user, and the code, off to an attacker-controlled site once it lands. Security researchers have documented this exact chain producing critical, high-payout findings against Facebook, Microsoft, Slack, and Dropbox.
A third variant runs through subdomains. If an authorization server trusts every subdomain under a given root domain, an attacker just needs to find an abandoned one, register it, and point it at their own infrastructure.
Correct implementation isn't complicated: exact-match URI comparison, no wildcards, no partial path matching. Redirect URIs get registered per client and checked against a strict allowlist, full stop. Anyone testing an OAuth flow should try appending a path, swapping in a sibling subdomain, and chaining with any open redirect on the registered domain. Three separate bypass surfaces, all three need checking.
Missing or unvalidated state parameters and the account-linking CSRF they enable
The state parameter exists to stop cross-site request forgery inside the OAuth flow. It's a value the client generates and sends with the authorization request. The server hands it back alongside the authorization code, and the client is supposed to check that the two match before doing anything else.
When state is missing, hardcoded, or just never checked on the way back, the callback turns into a CSRF target. Here's how that plays out: an attacker starts a legitimate OAuth flow but stops before finishing it, capturing the authorization URL. They trick a logged-in victim into opening that URL. The victim's own session finishes the flow, and the attacker's identity, or their third-party account, gets linked to the victim's account without the victim ever knowing it happened.
That's account-linking CSRF. The attacker can then log into the victim's account using their own credentials. They never need the victim's password.
Missing state validation isn't some rare finding tucked away in academic papers. It appears constantly in production OAuth audits, and checking for it is standard in any serious review. Fixing it means treating state exactly like a CSRF token on a web form: generate it randomly, tie it to the user's session, and check it server-side on every single callback.
Why implicit flow token leakage's deprecation hasn't made it disappear
In the implicit flow, the access token comes back straight in the URL fragment. It sits in browser history, in referrer headers, and often in server logs before the app even reads it.
OAuth 2.1 dropped implicit flow entirely, yet it's still everywhere. Why? For years it was the recommended pattern for single-page apps, and a lot of that code never got rewritten. It's also just easier to build than authorization code flow with PKCE, so under deadline pressure, developers reach for the simpler option. Some older libraries still default to it without anyone noticing.
Cross-site scripting makes this worse. A token sitting in the browser is reachable by any JavaScript running on the page, so a single XSS bug anywhere in the app becomes a path to stealing tokens. Security researchers have shown that referrer policy protections offer weaker guarantees than commonly assumed for fragment-based tokens. The exposure here runs bigger than most developers assume.
There's no configuration fix for this. Migration is the only real answer: move to authorization code flow with PKCE. No amount of tweaking implicit flow removes the basic problem of a token living in a URL. Anyone testing an OAuth setup should flag any client still requesting response_type=token as a finding on its own, no matter what other protections sit around it.
Persistent refresh tokens and the access that survives password resets and MFA re-enrollment
Access tokens are short-lived, usually gone within a few hours. Refresh tokens are the opposite: if the authorization server doesn't enforce rotation or a hard maximum lifetime, they can stick around for months or years.
That gap makes a stolen refresh token dangerous. It gives an attacker access that survives the victim changing their password, re-enrolling in MFA, even revoking active sessions, because none of those actions touch the refresh token directly.
The Salesloft-Drift breach is the cleanest example available. Persistent refresh tokens with no expiration, spread across more than 700 organizations, gave attackers ten days of active exfiltration before anyone caught it, according to reporting on the breach.
Token rotation is the fix. Every time a refresh token gets used to mint a new access token, the old refresh token should get invalidated and replaced. If someone tries reusing a refresh token that's already been invalidated, that should trigger revocation of the whole token family, logged and flagged for review.
Scope creep adds another layer to the problem. Refresh tokens often carry broader permissions than a user meant to grant long-term, so scope needs revalidating on every single refresh request, not assumed to still be correct months later. Anyone reviewing this should check maximum refresh token lifetime, confirm rotation actually happens, confirm revocation reaches resource servers, and confirm someone's reviewing scope grants.
What the nOAuth pattern reveals about identity assumptions in cross-tenant impersonation via mutable claims
The nOAuth vulnerability comes down to one bad assumption. Microsoft Entra ID allows users to have an email address that's never been verified, and if an application uses that email as its primary way of identifying a user, an attacker who sets their own Entra email to match a target's address can walk right into that target's account.
According to the Identity Management Institute, nearly 9% of Microsoft Entra SaaS apps remained vulnerable to this exact form of cross-tenant impersonation. The Semperis Security Research Team disclosed nine vulnerable applications in the Microsoft Entra App Gallery during a disclosure process that ran through 2024 and 2025, some holding personal data, Semperis reported.
The mistake is treating mutable attributes, email, display name, UPN, as if they were stable identifiers, instead of using the sub (subject) claim, which the identity provider guarantees stays unique and unchanged per user.
A related failure mode is domain resurrection. When a company's domain lapses, an attacker can register it, create email accounts matching former employees, and access any service still trusting that domain's email claims.
Fixing this means always using sub, or an equivalent immutable identifier, as the actual user identifier. Email should be treated as contact information, never as proof of identity. And the issuer (iss) claim needs checking against a known allowlist, so tokens from the wrong tenant can't slip through.
Third-party OAuth integrations as a supply chain attack surface
Every OAuth integration a SaaS product accepts is one more door into the building. If the company on the other side of that integration gets breached, the tokens they hold become the attacker's way in.
Salesloft-Drift is, again, the clearest case study here. The original break-in happened on Salesloft's GitHub, and the blast radius reached over 700 organizations through Drift integration tokens.
Reusing the same token across multiple downstream services without limiting its scope makes this worse: one compromised integrator now touches everything that token was ever authorized to reach. Newer attack patterns like cross-app OAuth account takeover (COAT) and cross-app request forgery (CORF), documented in OAuth 2.1 contexts per the Identity Management Institute, exploit platform-level account linking directly, getting a user to link a malicious app, then intercepting tokens meant for a legitimate one.
Even the device authorization flow, built originally for things like smart TVs and IoT hardware, has turned into a social engineering tool. Attackers impersonate IT staff, talk a victim into handing over a device authorization code, and walk away with persistent, token-based access. A correctly built OAuth flow can still be beaten by someone picking up the phone and lying well.
Organizations need to treat this as an ongoing audit: know who holds active tokens, what scopes those tokens carry, and when they were last used. Unused grants should get revoked. Every integration's security posture is, functionally, part of your own attack surface now.
What a real OAuth security review covers
Running a scanner is not the same thing as reviewing OAuth security, and treating the two as equivalent is where most reviews fall short. Automated tools catch the obvious stuff, missing HTTPS, an obvious wildcard in a redirect URI, but they can't reason through business logic, account-linking chains, or multi-step attack paths that only make sense once someone follows the whole flow end to end. A scanner doesn't know how to chain a weak redirect URI check together with an open redirect to actually prove token theft is possible. A person has to walk that path by hand.
A real OAuth test works through each layer. Redirect URI handling covers exact-match checks, wildcard attempts, path-append tricks, chaining with open redirects, and subdomain swaps. State parameter review confirms the parameter is present, is random, is tied to the session, and that the server actually checks it on callback. Grant type inventory flags any lingering implicit flow and confirms PKCE is enforced for public clients. Token lifetime review checks refresh token rotation, maximum lifetime limits, and whether revocation actually reaches resource servers. Identity claims review confirms sub gets used instead of email and checks that the issuer allowlist is enforced.
Whitebox access changes what's possible here. Looking at the authorization server's configuration, the code that issues tokens, and the callback handler itself, problems become visible that a blackbox tester watching HTTP traffic will never catch: a refresh token with no expiration policy at all, or a state parameter that gets generated but never actually checked against anything.
Quality penetration testing puts 60 to 80 percent of engagement time into manual work. Automated scanning feeds that process, but scanning by itself isn't the deliverable. A finding has to prove exploitability. Writing "redirect URI validation may be insufficient" without a working proof of concept isn't the same as showing an authorization code actually landing on an attacker's domain and getting exchanged for a live token.
Distinguishing a credible OAuth pen test engagement from a checkbox exercise
A credible OAuth engagement follows the flow the way an attacker would: redirect URI, state parameter, grant type, token lifetime, identity claims, third-party scope, in that order, with proof at every step. A checkbox exercise runs a scanner, lists known vulnerabilities, and calls it coverage. Those two things are not the same product, no matter what the invoice says.
The tell sits in the deliverable. A real report shows an authorization code landing somewhere it shouldn't, walks through the exact parameter that let it through, and demonstrates the token exchange working end to end. A checkbox report describes theoretical risk in passive language and stops there: no proof, no chain, no working exploit.
Find out what percentage of the engagement was manual versus automated. Ask whether the testers had whitebox access to the authorization server config or were stuck watching traffic from the outside. Ask whether the report draws a line between "this configuration looks weak" and "here is the authorization code we intercepted." An engagement that draws that line actually protects an organization. One that doesn't sits in a compliance folder, unread, until the next audit cycle rolls around.
Sources
- Common OAuth Vulnerabilities · Doyensec's Blog
- OAuth Vulnerabilities Every Security Team Should Know
- OAuth 2.1 Security Pitfalls - Identity Management Institute®
- semperis.com
- OAuth 2.0 authentication vulnerabilities | Web Security Academy
- auth0.com
- Salesloft Drift–Salesforce Breach (UNC6395): Why Salesforce OAuth Integrations are a Growing Risk
- descope.com

