# OAuth redirect_uri validation bypass

Redirect_uri checked by prefix, host, or regex before normalization, leaking codes or tokens

_Area: Web authentication and account takeover. Seen in 7 disclosed reports; 5 confirmed bounties, median $4,000, range $450-$7,500; latest 2023-06-11. Updated 2026-10-23._

## How it works
The OAuth server must decide whether a requested `redirect_uri` belongs to the registered client. When that check is a prefix match, host-only match, or unanchored regex applied before normalization, the assumption "this URI resolves to the client's own page" breaks. Traversal segments (`/../`), extra slashes, IDN lookalikes, or a path-embedded host let the code or token land somewhere the client never intended. The code is then redeemable because the token endpoint does not bind the code to the original redirect_uri [E1](https://bugbountylibrary.com/r/blog%3Ahomakov-blogspot-com-2014-02-how-i-hacked-github-again-html.md).

## Where it shows up
- redirect_uri validated by prefix, host, or substring rather than exact string equality [E1](https://bugbountylibrary.com/r/blog%3Ahomakov-blogspot-com-2014-02-how-i-hacked-github-again-html.md)[E3](https://bugbountylibrary.com/r/h1%3A1861974.md)[E5](https://bugbountylibrary.com/r/blog%3Ablog-intothesymmetry-com-2018-02-bug-bounty-left-over-and-rant-part-iii-html.md)
- Path traversal or `///host` sequences accepted without normalization [E1](https://bugbountylibrary.com/r/blog%3Ahomakov-blogspot-com-2014-02-how-i-hacked-github-again-html.md)[E3](https://bugbountylibrary.com/r/h1%3A1861974.md)[E5](https://bugbountylibrary.com/r/blog%3Ablog-intothesymmetry-com-2018-02-bug-bounty-left-over-and-rant-part-iii-html.md)
- Token endpoint that does not re-check redirect_uri against the one used at authorize time [E1](https://bugbountylibrary.com/r/blog%3Ahomakov-blogspot-com-2014-02-how-i-hacked-github-again-html.md)
- IDN/punycode hostnames compared without Unicode normalization [E4](https://bugbountylibrary.com/r/h1%3A861940.md)
- Regex host allowlist applied to `new URL(x).origin` without anchoring [E2](https://bugbountylibrary.com/r/blog%3Andevtk-github-io-writeups-2023-06-11-googlesource.md)
- Same-origin pages that log query strings (analytics, share widgets, referrer) reachable via traversal [E3](https://bugbountylibrary.com/r/h1%3A1861974.md)
- User-bindable custom domains reachable as a redirect target [E6](https://bugbountylibrary.com/r/h1%3A3596.md)

## How to review
The invariant: the redirect target must be exactly a registered URI, compared after full normalization (path, slashes, IDN), and the code must be bound to that same URI at redemption. Check that validation is exact-match, not prefix/host; that `..`, `//`, and encoded/Unicode variants are normalized before comparison; that the token endpoint rejects a code presented with a different redirect_uri; and that host allowlist regexes are anchored against the parsed origin [E2](https://bugbountylibrary.com/r/blog%3Andevtk-github-io-writeups-2023-06-11-googlesource.md).

## Variants seen
- Path traversal escaping a registered callback into an attacker page [E1](https://bugbountylibrary.com/r/blog%3Ahomakov-blogspot-com-2014-02-how-i-hacked-github-again-html.md)[E3](https://bugbountylibrary.com/r/h1%3A1861974.md)[E5](https://bugbountylibrary.com/r/blog%3Ablog-intothesymmetry-com-2018-02-bug-bounty-left-over-and-rant-part-iii-html.md)
- Prefix/host-only validation plus unbound code at token endpoint [E1](https://bugbountylibrary.com/r/blog%3Ahomakov-blogspot-com-2014-02-how-i-hacked-github-again-html.md)
- IDN homograph domain passing hostname comparison [E4](https://bugbountylibrary.com/r/h1%3A861940.md)
- Unanchored regex capturing a path-embedded attacker host [E2](https://bugbountylibrary.com/r/blog%3Andevtk-github-io-writeups-2023-06-11-googlesource.md)
- Code leaked through analytics/referer on a same-origin page [E3](https://bugbountylibrary.com/r/h1%3A1861974.md)
- User-controlled custom domain as redirect target [E6](https://bugbountylibrary.com/r/h1%3A3596.md)

## Why fixes fail
Fixing only the authorize endpoint leaves the token endpoint unbound, so a code obtained by any other leak is still redeemable [E1](https://bugbountylibrary.com/r/blog%3Ahomakov-blogspot-com-2014-02-how-i-hacked-github-again-html.md). Normalizing path traversal but not IDN (or vice versa) leaves the other bypass open [E4](https://bugbountylibrary.com/r/h1%3A861940.md).

## Impact and severity
Led to authorization-code and token theft and private repository exposure [E1](https://bugbountylibrary.com/r/blog%3Ahomakov-blogspot-com-2014-02-how-i-hacked-github-again-html.md), access-token hijack [E2](https://bugbountylibrary.com/r/blog%3Andevtk-github-io-writeups-2023-06-11-googlesource.md)[E5](https://bugbountylibrary.com/r/blog%3Ablog-intothesymmetry-com-2018-02-bug-bounty-left-over-and-rant-part-iii-html.md)[E6](https://bugbountylibrary.com/r/h1%3A3596.md), and code leakage to attacker domains [E3](https://bugbountylibrary.com/r/h1%3A1861974.md)[E4](https://bugbountylibrary.com/r/h1%3A861940.md). Worse when the code is redeemable without redirect_uri binding [E1](https://bugbountylibrary.com/r/blog%3Ahomakov-blogspot-com-2014-02-how-i-hacked-github-again-html.md).

Related but distinct: pre-approved clients silently granting escalated scopes [E1](https://bugbountylibrary.com/r/blog%3Ahomakov-blogspot-com-2014-02-how-i-hacked-github-again-html.md); spec-mandated error-path redirects acting as an unauthenticated open redirect; unexpected response_type/response_mode leaking tokens via fragment. These are separate classes, not redirect_uri validation bypass.

## Evidence
- E1: https://bugbountylibrary.com/r/blog%3Ahomakov-blogspot-com-2014-02-how-i-hacked-github-again-html.md
- E2: https://bugbountylibrary.com/r/blog%3Andevtk-github-io-writeups-2023-06-11-googlesource.md
- E3: https://bugbountylibrary.com/r/h1%3A1861974.md
- E4: https://bugbountylibrary.com/r/h1%3A861940.md
- E5: https://bugbountylibrary.com/r/blog%3Ablog-intothesymmetry-com-2018-02-bug-bounty-left-over-and-rant-part-iii-html.md
- E6: https://bugbountylibrary.com/r/h1%3A3596.md
