<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE rfc [
  <!ENTITY nbsp    "&#160;">
  <!ENTITY zwsp   "&#8203;">
  <!ENTITY nbhy   "&#8209;">
  <!ENTITY wj     "&#8288;">
]>
<!-- name="GENERATOR" content="github.com/mmarkdown/mmark Mmark Markdown Processor - mmark.miek.nl" -->
<rfc xmlns:xi="http://www.w3.org/2001/XInclude" version="3" ipr="trust200902" docName="draft-hardt-httpbis-redirect-headers-00" submissionType="IETF" category="std" xml:lang="en" indexInclude="true">

<front>
<title abbrev="Redirect Headers">HTTP Redirect Headers</title><seriesInfo value="draft-hardt-httpbis-redirect-headers-00" stream="IETF" status="standard" name="Internet-Draft"/>
<author initials="D." surname="Hardt" fullname="Dick Hardt"><organization>Hellō</organization><address><postal><street/>
</postal><email>dick.hardt@gmail.com</email>
</address></author><author initials="S." surname="Goto" fullname="Sam Goto"><organization>Google</organization><address><postal><street/>
</postal><email>goto@google.com</email>
</address></author><date/>
<area>Applications and Real-Time</area>
<workgroup>HTTP</workgroup>
<keyword>http</keyword>
<keyword>redirect</keyword>
<keyword>oauth</keyword>
<keyword>security</keyword>
<keyword>privacy</keyword>

<abstract>
<t>This document defines HTTP headers that enable secure parameter passing and mutual authentication during browser redirects. The Redirect-Query header carries parameters in browser-controlled headers instead of URLs, preventing leakage through browser history, Referer headers, server logs, and analytics systems. The Redirect-Origin header provides browser-verified origin authentication that cannot be spoofed or stripped, enabling reliable mutual authentication between parties. The optional Redirect-Path header allows servers to request path-specific origin verification. Together, these headers address critical security and privacy concerns in authentication and authorization protocols such as OAuth 2.0, OpenID Connect, and SAML.</t>
</abstract>

<note><name>Discussion Venues</name>
<t><em>Note: This section is to be removed before publishing as an RFC.</em></t>
<t>Source for this draft and an issue tracker can be found at <eref target="https://github.com/dickhardt/redirect-headers">https://github.com/dickhardt/redirect-headers</eref>.</t>
</note>

</front>

<middle>

<section anchor="introduction"><name>Introduction</name>
<t>Authentication and authorization protocols (OAuth <xref target="RFC6749"/>, OpenID Connect <xref target="OIDC"/>, SAML <xref target="SAML"/>) use browser redirects to navigate users between applications and authorization servers. These redirects must carry protocol parameters, which historically appear in URLs or POSTed forms.</t>
<t>This document addresses two distinct problems in redirect-based protocols:</t>

<ol>
<li><t><strong>Parameter Leakage</strong>: URL parameters leak sensitive data (authorization codes, tokens, session identifiers) through browser history, Referer headers, server logs, analytics systems, and JavaScript access. POST-based redirects expose parameters in DOM form fields. Both mechanisms make sensitive data visible to unintended parties.</t>
</li>
<li><t><strong>Origin Verification</strong>: Current mechanisms for verifying the origin of redirects are unreliable. The Referer header may be stripped, rewritten, or removed by privacy tools and enterprise proxies, preventing reliable mutual authentication between parties.</t>
</li>
</ol>
<t>This document defines two HTTP headers that address these problems:</t>

<ul spacing="compact">
<li><strong>Redirect-Query</strong> - Carries parameters in a browser-controlled header instead of URLs, preventing leakage while always being paired with origin verification</li>
<li><strong>Redirect-Origin</strong> - Provides browser-verified origin authentication that cannot be spoofed by scripts or manipulated by intermediaries</li>
</ul>
<t>A third header, <strong>Redirect-Path</strong>, allows servers to request path-specific origin verification when finer-grained validation is needed.</t>
<t><strong>Note on independent use</strong>: While origin verification without parameter passing is theoretically possible (server sends Redirect-Path, browser responds with Redirect-Origin), no specific use cases have been identified for this configuration.</t>
<t><strong>Incremental Deployment:</strong> A key feature of this specification is that deployment does not require coordination between parties. Each party (client application, browser, authorization server) can independently add support for Redirect Headers. Full functionality emerges naturally when all three parties support the mechanism, but partial deployment gracefully degrades to existing URL-based behavior. This allows for organic adoption without requiring synchronized upgrades across the ecosystem.</t>
</section>

<section anchor="redirect-headers"><name>Redirect Headers</name>
<t>Three headers work together during top-level 303 redirects to provide secure parameter passing and mutual authentication:</t>
<t><strong>Redirect-Query</strong> carries parameters from servers (client applications or authorization servers) to the browser, which then forwards them to the next party. This keeps sensitive data out of URLs while maintaining the redirect flow.</t>
<t><strong>Redirect-Origin</strong> provides browser-verified origin authentication. The browser sets this header when forwarding redirect parameters, allowing the receiving party to verify where the redirect originated. This cannot be spoofed by scripts or stripped by intermediaries.</t>
<t><strong>Redirect-Path</strong> (optional) allows servers to request path-specific origin verification for finer-grained validation within an origin.</t>
<t><strong>Browser behavior:</strong> Only processes these headers during top-level redirects. Ignores them for normal requests or embedded resources.</t>

<section anchor="redirect-query"><name>Redirect-Query</name>
<t>The Redirect-Query header carries redirect parameters using URL query string encoding. It is set by servers (either the client application or authorization server) in redirect responses.</t>

<artwork><![CDATA[Redirect-Query: "code=SplxlOBe&state=123"
]]></artwork>
<t><strong>Properties:</strong></t>

<ul spacing="compact">
<li>Set by server in HTTP 303 redirect response</li>
<li>Replaces URL query parameters</li>
<li>Parsed using standard URL query string parsing</li>
<li>Prevents exposure via browser history, Referer, logs, and analytics</li>
<li>When present, browser MUST include Redirect-Origin in the subsequent request</li>
</ul>
</section>

<section anchor="redirect-origin"><name>Redirect-Origin</name>
<t>The Redirect-Origin header provides browser-verified origin authentication. It is set ONLY by the browser and contains the origin (and optionally path) of the page from which the redirect originated.</t>
<t><strong>Format:</strong> Always ends with <tt>/</tt></t>
<t><strong>Properties:</strong></t>

<ul spacing="compact">
<li>Set ONLY by the browser (cannot be spoofed by scripts or intermediaries)</li>
<li>Enables receiving party to verify the origin of the redirect</li>
<li>Provides mutual authentication between parties</li>
<li>Always ends with <tt>/</tt> for consistent parsing</li>
<li>May include validated path when Redirect-Path is used</li>
</ul>
<t><strong>Browser behavior:</strong></t>
<t>The browser sets Redirect-Origin when either Redirect-Query or Redirect-Path is present in the redirect response.</t>

<section anchor="example-1-origin-only-verification-without-redirect-path"><name>Example 1: Origin-only verification (without Redirect-Path)</name>
<t><strong>Current page:</strong> <tt>https://app.example/some/page</tt></t>
<t><strong>Server sends redirect:</strong></t>

<artwork><![CDATA[HTTP/1.1 303 See Other
Location: https://as.example/authorize
Redirect-Query: "client_id=abc&state=123"
]]></artwork>
<t><strong>Browser forwards to AS:</strong></t>

<artwork><![CDATA[GET /authorize
Host: as.example
Redirect-Origin: "https://app.example/"
Redirect-Query: "client_id=abc&state=123"
]]></artwork>
<t>The Redirect-Origin is set to the origin plus <tt>/</tt>, without any path component.</t>
</section>

<section anchor="example-2-origin-path-verification-with-redirect-path"><name>Example 2: Origin+path verification (with Redirect-Path)</name>
<t><strong>Current page:</strong> <tt>https://app.example/mobile/dashboard</tt></t>
<t><strong>Server sends redirect:</strong></t>

<artwork><![CDATA[HTTP/1.1 303 See Other
Location: https://as.example/authorize
Redirect-Query: "client_id=abc&state=123"
Redirect-Path: "/mobile/"
]]></artwork>
<t><strong>Browser validates path claim:</strong>
- Redirect-Path claim: <tt>/mobile/</tt>
- Current page path: <tt>/mobile/dashboard</tt>
- Validation: Does <tt>/mobile/dashboard</tt> start with <tt>/mobile/</tt>? ✓ YES</t>
<t><strong>Browser forwards to AS:</strong></t>

<artwork><![CDATA[GET /authorize
Host: as.example
Redirect-Origin: "https://app.example/mobile/"
Redirect-Query: "client_id=abc&state=123"
]]></artwork>
<t>The Redirect-Origin includes the validated path <tt>/mobile/</tt> because the browser confirmed the current page is within that path.</t>
<t><strong>If path validation fails:</strong></t>
<t>If the current page was <tt>https://app.example/desktop/page</tt> and the server claimed <tt>Redirect-Path: "/mobile/"</tt>, the browser would reject the path claim and send only the origin:</t>

<artwork><![CDATA[Redirect-Origin: "https://app.example/"
]]></artwork>
</section>
</section>

<section anchor="redirect-path"><name>Redirect-Path</name>
<t>The Redirect-Path header allows a server to request path-specific origin verification. It is set by the server in the redirect response as a claim about the current page's path. The browser validates this claim and, if valid, includes the path in Redirect-Origin.</t>
<t><strong>Format:</strong> Must start with <tt>/</tt></t>

<artwork><![CDATA[Redirect-Path: "/app1"
]]></artwork>
<t><strong>Server behavior:</strong></t>
<t>The server includes Redirect-Path in the redirect response when it wants the receiving party to know not just the origin, but also a specific path within that origin.</t>
<t><strong>Browser validation:</strong></t>

<ol spacing="compact">
<li>Server sends: <tt>Redirect-Path: "/api"</tt></li>
<li>Browser checks: Does the current page path start with <tt>/api</tt>?</li>
<li>If valid: Include path in <tt>Redirect-Origin: "https://example.com/api/"</tt></li>
<li>If invalid: Ignore the path claim, use origin only: <tt>Redirect-Origin: "https://example.com/"</tt></li>
</ol>
<t>This mechanism prevents path manipulation attacks where an attacker might try to redirect from an unexpected path within the same origin. The server cannot lie about its path because the browser enforces validation.</t>
</section>
</section>

<section anchor="feature-discovery"><name>Feature Discovery</name>
<t>Some protocols may wish to discover browser support for Redirect Headers using Client Hints <xref target="RFC8942"/>.</t>
<t><strong>Server advertises support:</strong></t>

<artwork><![CDATA[Accept-CH: Redirect-Supported
]]></artwork>
<t><strong>Browser responds with capability:</strong></t>

<artwork><![CDATA[Redirect-Supported: ?1
]]></artwork>
<t><strong>Note:</strong> Feature discovery is optional and not required for OAuth flows. The incremental deployment model works without explicit discovery - authorization servers detect support by receiving Redirect-Query headers in requests.</t>
</section>

<section anchor="oauth-incremental-deployment"><name>OAuth Incremental Deployment</name>
<t>Redirect Headers is designed for <strong>incremental adoption</strong> - each party (client, browser, authorization server) can independently add support, with functionality emerging when all parties support it.</t>

<section anchor="how-it-works"><name>How It Works</name>
<t><strong>Clients</strong> can start sending parameters in both locations:</t>

<ul spacing="compact">
<li>URL query string (for backward compatibility)</li>
<li>Redirect-Query header (signaling support)</li>
</ul>
<t><strong>Browsers</strong> forward Redirect- headers when present:</t>

<ul spacing="compact">
<li>No special detection needed</li>
<li>Simply forward the headers during redirects</li>
</ul>
<t><strong>Authorization Servers</strong> detect support and respond accordingly:</t>

<ul spacing="compact">
<li>If request includes Redirect-Query → AS knows both client and browser support it</li>
<li>AS can then send response using <strong>only</strong> Redirect-Query header (no URL parameters)</li>
</ul>
</section>

<section anchor="adoption-path"><name>Adoption Path</name>
<t>Each party adds support independently, in any order:</t>
<t><strong>Clients</strong> add support by sending parameters in both the URL query string and the Redirect-Query header, and by looking for responses in the header while falling back to URL parameters.</t>
<t><strong>Browsers</strong> add support by forwarding Redirect-* headers when present during redirects. No configuration is needed.</t>
<t><strong>Authorization Servers</strong> add support by detecting when Redirect-Query is received (confirming that both the client and browser support it), then sending responses using only the Redirect-Query header and omitting URL parameters.</t>
<t><strong>Result:</strong> Once all three parties support it, the authorization code is sent in the header rather than the URL.</t>
<t><strong>No coordination required</strong> - each party adds support independently, and the system naturally converges to the secure behavior once all three support it. The client can immediately start sending both, browsers simply forward headers, and authorization servers detect support from incoming requests.</t>
</section>
</section>

<section anchor="oauth-redirect-security-threats"><name>OAuth Redirect Security Threats</name>
<t><strong>Scope:</strong> Redirect Headers specifically addresses OAuth and OIDC web-based redirect flows between websites where sensitive parameters are passed via URL query strings. This proposal does NOT address form_post mechanisms where data appears in the DOM, as that attack vector requires different mitigations.</t>

<section anchor="authorization-code-theft-from-url-query-strings"><name>Authorization Code Theft from URL Query Strings</name>
<t>The primary security weakness is in the <strong>authorization server's response</strong> containing the authorization code in the URL query string. When an AS redirects back to the client with ?code=...&amp;state=..., the authorization code is exposed through multiple vectors:</t>
<t><strong>Known attack vectors:</strong></t>

<ol>
<li><t><strong>Browser history leakage</strong> - Authorization codes stored in browser history can be retrieved by attackers with device access</t>

<ul spacing="compact">
<li>Reference: OAuth 2.0 Security Best Current Practice <xref target="RFC9700"/></li>
</ul></li>
<li><t><strong>Server log exposure</strong> - Authorization codes visible in web server access logs, proxy logs, and load balancer logs</t>

<ul spacing="compact">
<li>Codes can be extracted in real-time or from archived logs</li>
</ul></li>
<li><t><strong>Referer header leakage</strong> - When the callback page loads third-party resources (images, scripts, analytics), the authorization code leaks via the Referer header</t>

<ul spacing="compact">
<li>Reference: OAuth 2.0 authentication vulnerabilities <xref target="PORTSWIGGER-OAUTH"/></li>
</ul></li>
<li><t><strong>Browser-swapping attacks</strong> - Attackers exploit scenarios where authorization codes leak through shared URLs or when users switch browsers during the flow</t>

<ul spacing="compact">
<li>Discussion: OAuth-WG Browser-Swapping thread</li>
</ul></li>
<li><t><strong>URL sharing</strong> - Users may inadvertently share URLs containing authorization codes after errors or confusion</t>
</li>
<li><t><strong>Analytics and crash reporting</strong> - Authorization codes captured by analytics systems, error tracking, and monitoring tools</t>
</li>
</ol>
<t><strong>What Redirect Headers mitigates:</strong></t>
<t>By moving the authorization code from the URL query string to the Redirect-Query header, <strong>all of these attack vectors are eliminated</strong>. The authorization code never appears in:</t>

<ul spacing="compact">
<li>URLs (no browser history exposure)</li>
<li>Referer headers (no leakage to third parties)</li>
<li>Server logs (when servers are configured to not log sensitive headers)</li>
<li>User-visible locations (no accidental sharing)</li>
</ul>
<t><strong>Important clarification:</strong></t>
<t>The security concern is specifically the <strong>authorization server's response</strong> with the authorization code. The <strong>client's authorization request</strong> to the AS (containing client_id, redirect_uri, state) does not have known security concerns from being in the URL, as these parameters are not sensitive credentials. However, moving them to headers provides consistency and reduces URL clutter.</t>
</section>
</section>

<section anchor="security-considerations"><name>Security Considerations</name>

<section anchor="header-confidentiality"><name>Header Confidentiality</name>
<t>Redirect-Query carries sensitive parameters (authorization codes, tokens, session identifiers) that MUST be protected with the same care as credentials. Servers MUST:</t>

<ul spacing="compact">
<li>Configure logging systems to exclude or redact Redirect-Query header values</li>
<li>Treat Redirect-Query with the same confidentiality as Authorization headers</li>
<li>Use TLS for all redirects carrying Redirect-Query to prevent network observation</li>
</ul>
<t>Network intermediaries (proxies, load balancers, CDNs) can observe header values in transit. Deployment environments with untrusted intermediaries require additional protection beyond this specification.</t>
</section>

<section anchor="browser-implementation-requirements"><name>Browser Implementation Requirements</name>
<t>Browsers MUST enforce strict isolation of Redirect headers:</t>

<ul spacing="compact">
<li>JavaScript MUST NOT be able to read or set Redirect-* headers via any API</li>
<li>Browser extensions MUST NOT have access to Redirect-* headers</li>
<li>Only the browser's redirect handling mechanism can create or consume these headers</li>
<li>Headers MUST only be processed during top-level navigation redirects, never for subresource requests</li>
</ul>
<t>Failure to enforce these restrictions would allow malicious scripts to forge origin claims or steal sensitive parameters.</t>
</section>

<section anchor="origin-verification-limits"><name>Origin Verification Limits</name>
<t>Redirect-Origin provides browser-mediated mutual authentication but has limitations:</t>

<ul spacing="compact">
<li>It verifies the browser's understanding of origin, not the server's identity</li>
<li>It does not authenticate the user or establish a secure channel</li>
<li>It supplements but does NOT replace redirect_uri validation and registration</li>
<li>Servers MUST continue to validate redirect_uri against registered values</li>
</ul>
<t>Redirect-Path provides additional validation within an origin but cannot prevent attacks where the attacker controls a legitimate path within the same origin.</t>
</section>

<section anchor="browser-trust-model"><name>Browser Trust Model</name>
<t>This specification assumes an honest browser implementation. It cannot protect against:</t>

<ul spacing="compact">
<li>Compromised or malicious browsers</li>
<li>Browser bugs that fail to enforce header restrictions</li>
<li>Browser extensions with elevated privileges</li>
<li>Debugging tools that modify headers</li>
</ul>
<t>Servers should monitor for anomalous behavior (e.g., Redirect-Origin values that don't match expected patterns) as potential indicators of browser compromise or implementation bugs.</t>
</section>

<section anchor="transition-period-risks"><name>Transition Period Risks</name>
<t>During incremental deployment, clients may send parameters in both URL and headers for backward compatibility. This dual-sending pattern preserves URL leakage risks until:</t>

<ul spacing="compact">
<li>All parties (client, browser, server) support Redirect Headers</li>
<li>Clients stop including parameters in URLs</li>
</ul>
<t>Servers SHOULD detect Redirect-Query presence and warn or reject requests that also include sensitive parameters in URLs, to encourage migration away from URL-based parameters.</t>
</section>
</section>

<section anchor="privacy-considerations"><name>Privacy Considerations</name>

<section anchor="url-history-and-referer-leakage"><name>URL History and Referer Leakage</name>
<t>When parameters remain in URLs (as during transition or with non-supporting implementations), sensitive data persists in browser history and may leak via Referer headers. Implementers should consider:</t>

<ul spacing="compact">
<li>Browser history is persistent storage that may be accessed by malware, forensic tools, or unauthorized users with device access</li>
<li>Referer headers are sent automatically to third-party resources, potentially leaking parameters to analytics providers, CDNs, or advertisers</li>
<li>The transition to headers does not eliminate these risks until all parties stop sending parameters in URLs</li>
</ul>
<t>This specification does not eliminate all tracking vectors - cookies, browser fingerprinting, and other mechanisms remain unaffected.</t>
</section>

<section anchor="network-observer-privacy"><name>Network Observer Privacy</name>
<t>Network intermediaries can still observe Redirect-* headers in transit, just as they can observe URL parameters today. This specification does not enhance privacy against network-level observers (ISPs, proxies, corporate firewalls). TLS remains essential for protecting parameters from network observation.</t>
<t>In some cases, moving parameters to headers may slightly improve privacy: unlike URL paths (which may be visible in TLS SNI or DNS queries), HTTP headers are encrypted by TLS and not visible to passive network observers.</t>
</section>

<section anchor="redirect-origin-privacy-implications"><name>Redirect-Origin Privacy Implications</name>
<t>Redirect-Origin explicitly reveals the origin (and optionally path) of the redirecting page. Implementers should consider:</t>

<ul spacing="compact">
<li>This is similar to the Referer header but more reliable and always present when Redirect-Query is used</li>
<li>It enables the receiving party to know definitively where the redirect originated</li>
<li>Unlike Referer (which users/tools can strip for privacy), Redirect-Origin cannot be disabled by the user when Redirect-Query is present</li>
<li>This trade-off prioritizes security (mutual authentication) over origin hiding</li>
</ul>
<t>Protocols using Redirect Headers should only be deployed where mutual knowledge of party identities is acceptable and expected (as in OAuth, where the AS and client already know each other).</t>
</section>

<section anchor="user-control-and-transparency"><name>User Control and Transparency</name>
<t>Users have limited control over Redirect Headers:</t>

<ul spacing="compact">
<li>Users cannot inspect or modify Redirect-* headers (unlike URL parameters which are visible)</li>
<li>Users cannot selectively disable Redirect-Origin without breaking functionality</li>
<li>Browser developer tools may or may not expose these headers depending on implementation</li>
</ul>
<t>Browser implementations SHOULD provide visibility into Redirect Headers in developer tools for transparency, while maintaining the security restriction that JavaScript cannot access them.</t>
</section>

<section anchor="server-logging-practices"><name>Server Logging Practices</name>
<t>While Redirect Headers remove parameters from URLs (reducing accidental logging via URL-based logs), servers must implement appropriate logging controls:</t>

<ul spacing="compact">
<li>Configure web servers and load balancers to exclude Redirect-Query from access logs</li>
<li>Ensure application logging redacts sensitive header values</li>
<li>Be aware that default logging configurations may capture all headers</li>
</ul>
<t>The shift to headers does not automatically prevent logging - it requires conscious configuration changes.</t>
</section>
</section>

<section anchor="iana-considerations"><name>IANA Considerations</name>
<t>This document registers four new HTTP header fields in the "Hypertext Transfer Protocol (HTTP) Field Name Registry" defined in <xref target="RFC9110"/>.</t>

<section anchor="redirect-query-header-field"><name>Redirect-Query Header Field</name>
<t>Header field name: Redirect-Query</t>
<t>Applicable protocol: http</t>
<t>Status: standard</t>
<t>Author/Change controller: IETF</t>
<t>Specification document(s): [this document]</t>
</section>

<section anchor="redirect-origin-header-field"><name>Redirect-Origin Header Field</name>
<t>Header field name: Redirect-Origin</t>
<t>Applicable protocol: http</t>
<t>Status: standard</t>
<t>Author/Change controller: IETF</t>
<t>Specification document(s): [this document]</t>
</section>

<section anchor="redirect-path-header-field"><name>Redirect-Path Header Field</name>
<t>Header field name: Redirect-Path</t>
<t>Applicable protocol: http</t>
<t>Status: standard</t>
<t>Author/Change controller: IETF</t>
<t>Specification document(s): [this document]</t>
</section>

<section anchor="redirect-supported-header-field"><name>Redirect-Supported Header Field</name>
<t>Header field name: Redirect-Supported</t>
<t>Applicable protocol: http</t>
<t>Status: standard</t>
<t>Author/Change controller: IETF</t>
<t>Specification document(s): [this document]</t>
<t>Comments: Client Hint for feature discovery</t>
</section>
</section>

<section anchor="implementation-status"><name>Implementation Status</name>
<t><strong>Note to RFC Editor:</strong> Please remove this section before publication.</t>
<t><strong>Specification status:</strong> Exploratory draft</t>
<t><strong>Browser support:</strong> Not yet implemented (proposed specification)</t>
<t><strong>Server support:</strong> Reference implementations needed</t>
<t>This specification requires:</t>

<ul spacing="compact">
<li>Browser vendors to implement header handling</li>
<li>Authorization servers to support Redirect-Query</li>
<li>Client applications to adopt the pattern</li>
</ul>
<t>Deployment strategy: Backward compatible - clients send both URL and headers during transition.</t>
</section>

</middle>

<back>
<references><name>References</name>
<references><name>Normative References</name>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.6749.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.8942.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.9110.xml"/>
</references>
<references><name>Informative References</name>
<reference anchor="OIDC" target="https://openid.net/specs/openid-connect-core-1_0.html">
  <front>
    <title>OpenID Connect Core 1.0</title>
    <author fullname="N. Sakimura" initials="N." surname="Sakimura">
      <organization/>
    </author>
    <author fullname="J. Bradley" initials="J." surname="Bradley">
      <organization/>
    </author>
    <author fullname="M. Jones" initials="M." surname="Jones">
      <organization/>
    </author>
    <author fullname="B. de Medeiros" initials="B." surname="de Medeiros">
      <organization/>
    </author>
    <author fullname="C. Mortimore" initials="C." surname="Mortimore">
      <organization/>
    </author>
    <date year="2014" month="November"/>
  </front>
</reference>
<reference anchor="PORTSWIGGER-OAUTH" target="https://portswigger.net/web-security/oauth">
  <front>
    <title>OAuth 2.0 authentication vulnerabilities</title>
    <author>
      <organization>PortSwigger</organization>
    </author>
    <date year="2024"/>
  </front>
</reference>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.9700.xml"/>
<reference anchor="SAML" target="http://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf">
  <front>
    <title>Assertions and Protocols for the OASIS Security Assertion Markup Language (SAML) V2.0</title>
    <author fullname="S. Cantor" initials="S." surname="Cantor">
      <organization/>
    </author>
    <author fullname="J. Kemp" initials="J." surname="Kemp">
      <organization/>
    </author>
    <author fullname="R. Philpott" initials="R." surname="Philpott">
      <organization/>
    </author>
    <author fullname="E. Maler" initials="E." surname="Maler">
      <organization/>
    </author>
    <date year="2005" month="March"/>
  </front>
</reference>
</references>
</references>

<section anchor="oauth-example-before-and-after"><name>OAuth Example: Before and After</name>
<t>This appendix provides a detailed comparison of OAuth flows with and without Redirect Headers to illustrate the differences in security and functionality.</t>

<section anchor="without-redirect-headers-current-oauth"><name>Without Redirect Headers (current OAuth)</name>
<t><strong>Client Website returns to Browser:</strong></t>

<artwork><![CDATA[HTTP/1.1 303 See Other
Location: https://as.example/authorize?client_id=abc&state=123&redirect_uri=...
]]></artwork>
<t><strong>Browser navigates, sends to AS:</strong></t>

<artwork><![CDATA[GET /authorize?client_id=abc&state=123&redirect_uri=...
Host: as.example
Referer: https://app.example/login
]]></artwork>
<t>The Referer header is unreliable and may be stripped by browsers or proxies.</t>
<t><strong>AS returns code to Browser:</strong></t>

<artwork><![CDATA[HTTP/1.1 303 See Other
Location: https://app.example/cb?code=SplxlOBe&state=123
]]></artwork>
<t>The authorization code is now exposed in the URL.</t>
<t><strong>Browser sends code to Client Website:</strong></t>

<artwork><![CDATA[GET /cb?code=SplxlOBe&state=123
Host: app.example
Referer: https://as.example/consent
]]></artwork>
<t>The authorization code is stored in browser history, server logs, and analytics systems. Third-party resources loaded by this page will receive the code via the Referer header.</t>
<t><strong>Problems:</strong></t>

<ul spacing="compact">
<li>Authorization code appears in URL (history, logs, Referer, extensions)</li>
<li>No cryptographic origin verification (Referer is optional and unreliable)</li>
</ul>
</section>

<section anchor="with-redirect-headers"><name>With Redirect Headers</name>
<t><strong>Client Website returns to Browser:</strong></t>

<artwork><![CDATA[HTTP/1.1 303 See Other
Location: https://as.example/authorize
Redirect-Query: "client_id=abc&state=123&redirect_uri=https://app.example/portal/callback"
Redirect-Path: "/portal/"
]]></artwork>
<t><strong>Browser validates and adds origin:</strong></t>
<t>The browser validates the Redirect-Path claim against the current page URL. In this example, the current page is <tt>https://app.example/portal/login</tt> and the Redirect-Path claim is <tt>/portal/</tt>. Since the page path starts with <tt>/portal/</tt>, validation succeeds and the browser sets Redirect-Origin to <tt>https://app.example/portal/</tt>.</t>
<t><strong>Browser navigates, sends to AS:</strong></t>

<artwork><![CDATA[GET /authorize
Host: as.example
Redirect-Origin: "https://app.example/portal/"
Redirect-Query: "client_id=abc&state=123&redirect_uri=https://app.example/portal/callback"
]]></artwork>
<t>The Redirect-Origin is browser-supplied and cannot be spoofed. Parameters are transmitted in headers, not in the URL.</t>
<t><strong>AS validates and returns to Browser:</strong></t>
<t>The AS verifies that Redirect-Origin is <tt>https://app.example/portal/</tt> and that the redirect_uri starts with <tt>https://app.example/portal/</tt>.</t>

<artwork><![CDATA[HTTP/1.1 303 See Other
Location: https://app.example/portal/callback
Redirect-Query: "code=SplxlOBe&state=123"
]]></artwork>
<t>The authorization code is transmitted in the header. No parameters appear in the URL.</t>
<t><strong>Browser forwards back to Client Website:</strong></t>
<t>The browser is now on the AS consent page at <tt>https://as.example/consent</tt>. Since Redirect-Query is present in the response, the browser sets Redirect-Origin to <tt>https://as.example/</tt>.</t>

<artwork><![CDATA[GET /portal/callback
Host: app.example
Redirect-Origin: "https://as.example/"
Redirect-Query: "code=SplxlOBe&state=123"
]]></artwork>
<t>The URL is clean with no query parameters. The client verifies that Redirect-Origin matches the expected AS. The authorization code never appears in the URL, browser history, or Referer headers.</t>
<t><strong>Benefits:</strong></t>

<ul spacing="compact">
<li>Authorization code never appears in URLs</li>
<li>Mutual origin authentication (browser-verified)</li>
<li>Backward compatible (browsers/servers without support fall back to URL parameters)</li>
</ul>
<t><strong>Requirements:</strong></t>

<ul spacing="compact">
<li>If Redirect-Query received in request: AS MUST use Redirect-Query for response</li>
<li>Client MUST verify Redirect-Origin matches expected AS</li>
<li>AS MUST verify Redirect-Origin matches expected client</li>
<li>When Redirect-Query is present, client MUST ignore URL parameters and use only header parameters</li>
</ul>
</section>
</section>

<section anchor="acknowledgments"><name>Acknowledgments</name>
<t>The authors would like to thank early reviewers for their valuable feedback and insights that helped shape this proposal: Jonas Primbs, Warren Parad.</t>
</section>

</back>

</rfc>
