# OAuth redirect check vs normalization mismatch

Redirect target validated as raw string but normalized differently when consumed, or skipped on error/empty paths

_Area: Web authentication and account takeover. Seen in 2 disclosed reports; latest 2026-10-15. Updated 2026-10-24._

## How it works
A redirect target (redirect_uri, next, target_uri) is checked against an allowlist by comparing the raw string, but the value is later interpreted by a different component — browser, framework, or downstream service — that normalizes it differently. The check and the use disagree about what the string means, so a value that passes resolves to an attacker destination. The same gap appears when error or empty-input paths skip validation, or when a missing value is treated as trusted.

## Where it shows up
- Redirect/next parameters validated by prefix or substring match, then placed in a Location header or window loader.
- Scheme or host comparisons that are case-sensitive (uppercase `HTTPS://`) or that ignore backslashes (`/\host`).
- Origin checks based on Referer that accept an empty or absent Referer as valid.
- OAuth error responses that redirect to the registered redirect_uri without consent.
- Desktop/Electron allowlists that trust "same origin" and follow redirects after the check.

## How to review
Find every place a redirect target is checked and every place it is consumed. The invariant: the exact value that reaches the browser or loader must be the value that was validated, after full normalization. Name the steps per component:
- Scheme: compare case-insensitively.
- Path: convert backslashes to slashes before matching.
- Host: parse with a real URL parser, not string prefix.
- Validation runs on all paths, including error responses and missing/empty inputs.
- Empty or absent origin indicators fail closed.
- Same-origin trust does not survive a redirect; re-check after any 3xx.

## Variants seen
- Canonical normalization case: `next` filter blocks `//` but not `/\host`, which browsers normalize to `//` in Location headers [E4](https://bugbountylibrary.com/r/h1%3A1782514.md).
- Case-sensitive scheme check in proxy_image 302s uppercase `HTTPS://` past a desktop allowlist, and same-origin trust survives the redirect into an Electron window [E1](https://bugbountylibrary.com/r/h1%3A3972760.md).
- Missing-input fail-open: empty Referer accepted as valid origin on an OAuth bridge [E2](https://bugbountylibrary.com/r/h1%3A168116.md).
- Spec-mandated error-path redirect to registered redirect_uri acts as an open redirect pre-consent [E3](https://bugbountylibrary.com/r/h1%3A26962.md).

## Why fixes fail
Filters that handle only forward slashes [E4](https://bugbountylibrary.com/r/h1%3A1782514.md) or only lowercase schemes [E1](https://bugbountylibrary.com/r/h1%3A3972760.md) leave equivalent encodings open; treating absent input as valid [E2](https://bugbountylibrary.com/r/h1%3A168116.md) and validating only the happy path [E3](https://bugbountylibrary.com/r/h1%3A26962.md) leave the same gap.

## Impact and severity
Led to loading attacker content inside a trusted desktop window [E1](https://bugbountylibrary.com/r/h1%3A3972760.md), OAuth credential theft [E2](https://bugbountylibrary.com/r/h1%3A168116.md), and open redirects usable pre-consent [E3](https://bugbountylibrary.com/r/h1%3A26962.md)[E4](https://bugbountylibrary.com/r/h1%3A1782514.md). Severity is high where the redirect lands inside a trusted origin or yields tokens; open redirects alone are lower but become serious when chained with same-origin trust or credential-bearing flows.

## Evidence
- E1: https://bugbountylibrary.com/r/h1%3A3972760.md
- E2: https://bugbountylibrary.com/r/h1%3A168116.md
- E3: https://bugbountylibrary.com/r/h1%3A26962.md
- E4: https://bugbountylibrary.com/r/h1%3A1782514.md
