RSS Amplifier

Alex Fadeev · Jul 29, 2026

Hardening OAuth Flows Against Configuration Errors in 2026

0
Sign in to vote or save

Alex Fadeev · Alex Fadeev

OAuth failures usually do not come from the protocol itself. They come from how teams wire it up. In practice, one input tends to dominate the risk surface: redirect_uri. If an authorization server accepts values that are not locked to an exact per-client allowlist entry, an attacker can steer an authorization code or token to infrastructure they control. That can end in account takeover even when the client secret never leaks. ⚠️

A solid defensive review is mostly a search for four things: loose redirect_uri checks, missing PKCE, weak or absent state validation, and risky token handling after issuance. The most useful monitoring signals follow the same pattern: callback URLs that fall outside the registered allowlist, and tokens that suddenly appear from a network or region that does not fit the normal profile.

This is defensive guidance for authorized environments only. The goal is to help defenders audit implementations, spot abuse, and close gaps early. The overall standard to line up with is RFC 9700, published in January 2025, and the behavior maps cleanly to MITRE ATT&CK T1528, T1550.001, and T1078.

  • The biggest OAuth risk is weak redirect_uri validation. Match the complete URI per client: scheme, host, port, and path. No wildcards. No regex.

  • PKCE should be required everywhere. If an attacker intercepts the authorization code but lacks the verifier, the code is useless.

  • state must be generated and checked. It is your CSRF and login-fixation defense.

  • Validate redirect_uri twice. Check it at the authorize step and again during token exchange.

  • Detection is straightforward if you log the right fields. Watch for off-allowlist callback requests and tokens used from a new network or geography right after issuance. ✅

An OAuth misconfiguration is an implementation flaw in either the client or the authorization server that allows someone to obtain a code, token, or effective account access they should not have. The protocol design is not the problem here. The breakdown is almost always in validation, policy, or storage.

Typical failure points include:

  • accepting the wrong redirect_uri

  • not enforcing PKCE

  • missing state checks

  • granting scopes that are too broad

  • storing tokens in unsafe places

Because OAuth tokens effectively represent identity and delegated access, these mistakes are not minor. They are often direct account-takeover conditions. 🚨

Among these controls, redirect_uri matters most because it defines where the server sends sensitive material after authentication. If that destination can be manipulated, the server is doing the attacker’s delivery work for them.

Across real implementations, the same categories appear repeatedly:

  • Loose redirect_uri validation

- The attacker gets the code or token sent to a server they control. - Review signal: the requested callback is not an exact match for the registered allowlist. - Fix: exact per-client allowlist matching.

  • No PKCE

- The attacker intercepts the authorization code and redeems it. - Review signal: a public client exchanging codes without PKCE. - Fix: require PKCE for all clients.

  • Missing or unchecked state

- The attacker abuses CSRF or login-fixation behavior. - Review signal: authorization requests without a valid state, or callbacks where state is ignored. - Fix: generate it, bind it to the session, and validate it on return.

  • Implicit flow still enabled

- Tokens can be exposed in the browser URL fragment. - Review signal: response_type=token in authorize traffic. - Fix: use authorization code flow with PKCE.

  • Scopes are broader than necessary or pre-consent is overly permissive

- The token provides more access than the client actually needs. - Review signal: granted scopes exceed the normal baseline. - Fix: least-privilege scope design. 📌

The pattern underneath all of this is simple: strict validation plus least privilege.

Public breach research has shown how dangerous partial validation can be. A common pattern is checking only the callback domain while ignoring the exact path. That sounds close to safe, but it is not. If the path is uncontrolled, redirect chains can still leak authorization material.

The practical lesson is not subtle: validate the entire redirect_uri string, not just part of it. That means origin and path together, matched exactly. Then perform the same validation again when the client exchanges the code for a token over the back channel. That second check matters because it closes a gap the attacker cannot easily reach remotely. 🛠️

Two signals are especially valuable:

1. an authorize request that carries a redirect_uri outside the client’s registered allowlist 2. a token that is used from an unfamiliar place shortly after it was issued

If your logs capture the requested callback URL and whether it matches the client’s registered configuration, a mismatch is either an attack attempt or a broken integration that still needs immediate review.

title: OAuth Authorize Request With an Off-Allowlist redirect_uri
id: 11111111-darkpwn-illustrative
status: experimental
logsource:
  product: application
  service: oauth
detection:
  selection:
    endpoint|contains: '/authorize'
  suspect:
    redirect_uri|contains: ['@', '%2f', '%2F', '/../', 'localhost', '..']
  mismatch:
    redirect_uri_allowlisted: 'false'
  condition: selection and (suspect or mismatch)
falsepositives:
  - Newly registered legitimate callback URLs (update the allowlist, then this clears)
level: high

This detection works because off-allowlist redirects and obvious traversal or redirect tricks leave recognizable request-time artifacts. Even when an attempt is not malicious, you still want it surfaced quickly. ⚠️

Once a token is stolen, the API traffic often looks perfectly normal. The requests can be syntactically identical to legitimate use. That means content matching is weak; behavior is where the signal lives.

index=oauth (event=token_issued OR event=token_used)
| transaction token_id maxspan=15m
| eval issue_net=mvindex(src_subnet,0), use_net=mvindex(src_subnet,-1)
| where issue_net!=use_net
| table _time, client_id, token_id, issue_net, use_net, scope

The idea is to compare the network at issuance with the network seen during early token use. A mismatch inside a short window can be a strong indicator of compromise. For mature detection, build baselines around:

  • source network

  • geography

  • refresh cadence

  • normal scope usage

Then alert on deviation: a new subnet after the token is established, impossible geographic movement, or scope activity that exceeds the token’s historical pattern. ✅

When you review an OAuth implementation, inspect both every client and the authorization server.

  • redirect_uri

- Confirm exact matching against a per-client allowlist. - Reject wildcards, regex, and path-prefix logic. - Test bypass candidates like @evil.com, app.com.evil.com, %2f, and /../.

  • PKCE

- Verify it is mandatory for all clients, not only public ones.

  • state

- Confirm generation, session binding, and return-path validation.

  • Flow choice

- Make sure implicit flow is disabled. - Prefer authorization code flow with PKCE.

  • Token exchange

- Verify redirect_uri is checked again during code redemption.

  • Configuration hygiene

- Flag localhost callbacks, wildcard redirects, unnecessary pre-consent, and excessive default scopes. 🛠️

The core defensive pattern from RFC 9700 is straightforward:

  • exact-match redirect_uri validation

  • PKCE for every client

  • validated state

  • no implicit flow

  • second redirect_uri check at token exchange

A few additional controls matter just as much operationally:

  • grant only the scopes the client actually needs

  • store tokens safely, ideally in HttpOnly cookies rather than local storage

  • support token revocation

  • on mobile, prefer Android App Links and iOS Associated Domains instead of raw custom URI schemes 🚀

Teams repeatedly miss the same issues:

  • They do not log authorize requests.

- If you cannot see the callback request, you will miss off-allowlist activity entirely.

  • They try to detect stolen tokens from content alone.

- Legitimate and stolen tokens usually produce the same API calls.

  • They rely on regex or wildcard callback validation.

- This is one of the most common root causes of bypass.

  • They treat the review as a one-off task.

- These flaws can sit quietly for months before anyone notices. 📌

Use this as a recurring review baseline:

  • Enforce exact-match redirect_uri validation for scheme, host, port, and path on a per-client basis.

  • Disallow wildcard and regex callback matching, and require HTTPS.

  • Require PKCE for all clients, validate state, and disable implicit flow.

  • Check redirect_uri again during token exchange.

  • Apply least-privilege scopes and remove unnecessary pre-consent.

  • Log authorize requests and alert on off-allowlist or open-redirect-style redirect_uri values.

  • Baseline token use by network, geo, and scope; alert on post-issuance deviation.

  • Store tokens in HttpOnly cookies, support revocation, and align the overall design to RFC 9700. ✅

The highest-return control is a recurring configuration audit. Wildcard callbacks, localhost redirects, missing PKCE, and bloated scopes are often sitting in plain sight long before anyone exploits them.

If you only remember one thing, make it this: an OAuth defensive review is primarily a redirect_uri review. Start there, then verify PKCE everywhere, enforce state, and confirm the callback is checked again during token exchange. Detection should cover both the front of the flow and the aftermath: bad callback destinations at request time, and abnormal token behavior after issuance. 🔒

Weak redirect_uri validation is usually the most severe. If the authorization server does not require an exact allowlist match for each client, an attacker can redirect the code or token to infrastructure they control. The full callback URI must be checked: scheme, host, port, and path.

Log authorize requests and alert when a requested callback is not an exact match for the client’s registered allowlist. Also look for open-redirect indicators such as @, %2f, extra subdomains, traversal patterns, and tokens used from a new network or geography shortly after issuance.

Yes. PKCE binds the authorization code to a code_verifier the attacker does not possess, which prevents successful token exchange even if the code is intercepted. RFC 9700 recommends PKCE for both public and confidential clients.

RFC 9700 is the OAuth 2.0 Security Best Current Practice document released in January 2025. It formalizes defensive lessons learned from real-world failures, including exact callback matching, PKCE for all clients, avoidance of implicit flow, and validation of the state parameter.

🔍 TL;DR Summary

  • 🔐 The most critical OAuth review item is exact-match redirect_uri validation per client.

  • 🧷 PKCE should be mandatory everywhere, and state must be generated and validated.

  • 🚫 Avoid implicit flow and check redirect_uri again during token exchange.

  • 🕵️ Detect abuse by logging authorize requests and flagging off-allowlist callbacks.

  • 🌐 Stolen tokens are best found through behavior: new networks, impossible geo shifts, and unusual scope use after issuance.

No posts

Read the original on afadeev.substack.com

Comments

Nothing yet. Say the first thing.

    Sign in to join the conversation.