OAuth Protected Authorization
draft-hardt-oauth-protected-authorization-00
This document is an Internet-Draft (I-D).
Anyone may submit an I-D to the IETF.
This I-D is not endorsed by the IETF and has no formal standing in the
IETF standards process.
| Document | Type | Active Internet-Draft (individual) | |
|---|---|---|---|
| Authors | Dick Hardt , Sam Goto | ||
| Last updated | 2026-07-04 | ||
| Replaces | draft-hardt-httpbis-redirect-headers | ||
| RFC stream | (None) | ||
| Intended RFC status | (None) | ||
| Formats | |||
| Stream | Stream state | (No stream defined) | |
| Consensus boilerplate | Unknown | ||
| RFC Editor Note | (None) | ||
| IESG | IESG state | I-D Exists | |
| Telechat date | (None) | ||
| Responsible AD | (None) | ||
| Send notices to | (None) |
draft-hardt-oauth-protected-authorization-00
Web Authorization Protocol D. Hardt
Internet-Draft Hellō
Intended status: Standards Track S. Goto
Expires: 5 January 2027 Google
4 July 2026
OAuth Protected Authorization
draft-hardt-oauth-protected-authorization-00
Abstract
This document defines browser support for protecting OAuth 2.0
authorization requests and authorization responses during redirect-
based authorization flows. A single Structured Field header field,
OAuth-Authorization, is set by the OAuth client in the redirect
response that sends the browser to the authorization server, and by
the authorization server in the redirect response that returns the
browser to the OAuth client. In both cases the browser augments the
header with the attested origin of the redirecting party and delivers
it to the redirect destination. The mechanism provides security for
the authorization request: the authorization server receives a
browser-attested, tamper-evident statement of which origin initiated
the request. The mechanism provides security and privacy for the
authorization response: the authorization code is delivered in the
browser-protected header instead of the redirect URI, and never
appears in a URL, eliminating its exposure through browser history,
server logs, Referer headers, analytics systems, and URL sharing.
The header is generated, validated, and delivered by the browser, and
is inaccessible to scripts, service workers, and browser extensions.
Existing OAuth deployments continue to function unchanged; the
protections activate only when the OAuth client, browser, and
authorization server all support them.
Discussion Venues
_Note: This section is to be removed before publishing as an RFC._
Source for this draft and an issue tracker can be found at
https://github.com/dickhardt/oauth-protected-authorization
(https://github.com/dickhardt/oauth-protected-authorization).
Status of This Memo
This Internet-Draft is submitted in full conformance with the
provisions of BCP 78 and BCP 79.
Hardt & Goto Expires 5 January 2027 [Page 1]
Internet-Draft Protected Authorization July 2026
Internet-Drafts are working documents of the Internet Engineering
Task Force (IETF). Note that other groups may also distribute
working documents as Internet-Drafts. The list of current Internet-
Drafts is at https://datatracker.ietf.org/drafts/current/.
Internet-Drafts are draft documents valid for a maximum of six months
and may be updated, replaced, or obsoleted by other documents at any
time. It is inappropriate to use Internet-Drafts as reference
material or to cite them other than as "work in progress."
This Internet-Draft will expire on 5 January 2027.
Copyright Notice
Copyright (c) 2026 IETF Trust and the persons identified as the
document authors. All rights reserved.
This document is subject to BCP 78 and the IETF Trust's Legal
Provisions Relating to IETF Documents (https://trustee.ietf.org/
license-info) in effect on the date of publication of this document.
Please review these documents carefully, as they describe your rights
and restrictions with respect to this document. Code Components
extracted from this document must include Revised BSD License text as
described in Section 4.e of the Trust Legal Provisions and are
provided without warranty as described in the Revised BSD License.
Table of Contents
1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . 4
1.1. Goals . . . . . . . . . . . . . . . . . . . . . . . . . . 5
1.2. Non-Goals . . . . . . . . . . . . . . . . . . . . . . . . 5
1.3. Browser Enforcement Precondition . . . . . . . . . . . . 6
2. Conventions and Terminology . . . . . . . . . . . . . . . . . 6
3. Protocol Overview . . . . . . . . . . . . . . . . . . . . . . 7
4. The OAuth-Authorization Header Field . . . . . . . . . . . . 7
4.1. query . . . . . . . . . . . . . . . . . . . . . . . . . . 8
4.2. origin . . . . . . . . . . . . . . . . . . . . . . . . . 8
4.3. path . . . . . . . . . . . . . . . . . . . . . . . . . . 8
5. Authorization Request . . . . . . . . . . . . . . . . . . . . 8
5.1. Client Behavior . . . . . . . . . . . . . . . . . . . . . 9
5.2. Authorization Server Behavior . . . . . . . . . . . . . . 9
6. Authorization Response . . . . . . . . . . . . . . . . . . . 10
6.1. Authorization Server Behavior . . . . . . . . . . . . . . 10
6.2. Client Behavior . . . . . . . . . . . . . . . . . . . . . 11
7. Path Validation . . . . . . . . . . . . . . . . . . . . . . . 12
8. Browser Processing Model . . . . . . . . . . . . . . . . . . 12
9. Deployment Considerations . . . . . . . . . . . . . . . . . . 14
9.1. Incremental Adoption . . . . . . . . . . . . . . . . . . 14
Hardt & Goto Expires 5 January 2027 [Page 2]
Internet-Draft Protected Authorization July 2026
9.2. Dual Transmission of Request Parameters . . . . . . . . . 14
9.3. Single-Page Applications . . . . . . . . . . . . . . . . 14
9.4. Header Sizes . . . . . . . . . . . . . . . . . . . . . . 15
10. Security Considerations . . . . . . . . . . . . . . . . . . . 15
10.1. Dependence on Browser Enforcement . . . . . . . . . . . 15
10.2. Downgrade . . . . . . . . . . . . . . . . . . . . . . . 15
10.3. Cross-Context Delivery . . . . . . . . . . . . . . . . . 15
10.4. What Is and Is Not Protected . . . . . . . . . . . . . . 16
10.5. Server-Side Handling . . . . . . . . . . . . . . . . . . 16
10.6. Relationship to Existing Mechanisms . . . . . . . . . . 17
11. Privacy Considerations . . . . . . . . . . . . . . . . . . . 17
11.1. Authorization Request Privacy . . . . . . . . . . . . . 17
11.2. Authorization Response Privacy . . . . . . . . . . . . . 17
11.3. No New Cross-Site Information Flow . . . . . . . . . . . 17
11.4. Origin and Path Disclosure . . . . . . . . . . . . . . . 18
11.5. User Transparency . . . . . . . . . . . . . . . . . . . 18
12. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 18
12.1. OAuth-Authorization Header Field . . . . . . . . . . . . 18
13. Implementation Status . . . . . . . . . . . . . . . . . . . . 18
14. Acknowledgments . . . . . . . . . . . . . . . . . . . . . . . 19
15. References . . . . . . . . . . . . . . . . . . . . . . . . . 19
15.1. Normative References . . . . . . . . . . . . . . . . . . 19
15.2. Informative References . . . . . . . . . . . . . . . . . 19
Appendix A. Example: Authorization Code Flow Before and After . 20
A.1. Without This Mechanism (Current OAuth) . . . . . . . . . 20
A.2. With This Mechanism . . . . . . . . . . . . . . . . . . . 21
Appendix B. Design Rationale . . . . . . . . . . . . . . . . . . 22
B.1. Why OAuth-Specific Rather Than Generic Redirect
Headers . . . . . . . . . . . . . . . . . . . . . . . . 22
B.2. Why a Single Header Field . . . . . . . . . . . . . . . . 22
B.3. Why Not Cryptographic Protection of Parameters . . . . . 22
B.4. Why Not Move to the Back Channel . . . . . . . . . . . . 23
B.5. Why the Authorization Request Parameters Stay in the
URL . . . . . . . . . . . . . . . . . . . . . . . . . . 23
B.6. Why a Structured Field Dictionary Wrapping a Query
String . . . . . . . . . . . . . . . . . . . . . . . . . 23
B.7. Why Redirect Responses Only (Why Not form_post) . . . . . 23
B.8. Why the AS Rejects on Query Mismatch, Rather Than the
Browser Dropping the Header . . . . . . . . . . . . . . 24
B.9. Why Not a Sec-Prefixed Name . . . . . . . . . . . . . . . 24
B.10. Why Not a response_mode Value . . . . . . . . . . . . . . 24
B.11. Why the Authorization Response Is Not Readable by
JavaScript . . . . . . . . . . . . . . . . . . . . . . . 24
B.12. Why Native Application Flows Are Out of Scope . . . . . . 24
B.13. Why Browser-Attested Origin When iss Exists . . . . . . . 25
Authors' Addresses . . . . . . . . . . . . . . . . . . . . . . . 25
Hardt & Goto Expires 5 January 2027 [Page 3]
Internet-Draft Protected Authorization July 2026
1. Introduction
OAuth 2.0 [RFC6749] redirect-based authorization flows carry protocol
parameters in URLs. The critical parameter is the *authorization
code*: it is a credential, exchangeable for tokens. When the
authorization server redirects back to the OAuth client with
?code=...&state=... in the redirect URI, that credential is written
into browser history, web server access logs, proxy and load balancer
logs, and analytics systems; it leaks through Referer headers to
third-party resources loaded by the callback page; and it can be
disclosed through URL sharing, screenshots, and copy/paste. The
OAuth 2.0 Security Best Current Practice [RFC9700] documents these
leakage vectors and prescribes mitigations that limit the damage of a
leaked code (PKCE [RFC7636], one-time use, short lifetimes), but the
code is still exposed.
A second, related weakness is that the authorization server has no
reliable way to know which web origin sent the user. The Referer
header may be trimmed, stripped, or rewritten, and everything else in
the authorization request is claimed by the requester rather than
attested by anyone.
This document defines browser behavior and a single Structured Field
[RFC9651] header field, OAuth-Authorization, used on both legs of the
flow:
* For the *authorization request*, the OAuth client sets the header
in the redirect response that sends the browser to the
authorization server. The browser delivers it with the
navigation, adding a browser-attested origin (and optionally a
browser-validated path) identifying where the navigation actually
came from. Its presence also signals to the authorization server
that the browser and OAuth client support protected authorization
responses.
* For the *authorization response*, the authorization server sets
the header in the redirect response that returns the browser to
the OAuth client, placing the authorization response parameters in
the header instead of the redirect URI. The browser delivers it,
exactly once, to the redirect URI, adding a browser-attested
origin identifying the authorization server.
The browser performs the same processing in both cases (Section 8);
which OAuth message the header carries is determined by the endpoint
that receives it.
The mechanism provides *security for the authorization request, and
security and privacy for the authorization response*:
Hardt & Goto Expires 5 January 2027 [Page 4]
Internet-Draft Protected Authorization July 2026
* The authorization request parameters remain in the request URI,
unchanged, so existing authorization servers continue to work.
The header adds origin attestation, tamper evidence, and a
capability signal. This is security without privacy: the OAuth
client cannot know whether a given user's browser supports this
mechanism, so the parameters never leave the URL.
* The authorization response parameters, above all the authorization
code, exist only in the protected header and never appear in any
URL. The authorization server sends them this way only after the
header's arrival with the authorization request has proven that
the browser and OAuth client support it.
Existing deployments continue to function unchanged, and adoption is
deliberately inexpensive: for most clients, support arrives as an
update to the OAuth library they already use, with no new endpoints,
no keys, and no change to how authorization parameters are
constructed or parsed (Section 9).
1.1. Goals
1. Provide an explicit browser signal that a navigation is an OAuth
authorization request.
2. Provide a protected, browser-mediated delivery mechanism for
OAuth authorization responses.
3. Preserve complete compatibility with existing OAuth deployments
and parameter processing.
This mechanism complements PKCE [RFC7636], PAR [RFC9126], iss
identification [RFC9207], and JARM [JARM]. It does not replace any
of them.
1.2. Non-Goals
* This is not a back-channel protocol. Protocols that move the
authorization exchange out of the front channel entirely (such as
PAR [RFC9126] for the request, or the AAuth protocol
[I-D.hardt-oauth-aauth-protocol] for the full authorization
exchange) provide stronger confidentiality with different
deployment costs. See Appendix B.4.
* This is not a generic HTTP redirect mechanism. It is
intentionally specific to OAuth authorization flows. See
Appendix B.1.
* This mechanism does not protect against a compromised or non-
conforming browser, a malicious authorization server, or a
malicious OAuth client.
* Native application flows [RFC8252] are out of scope. See
Appendix B.12.
Hardt & Goto Expires 5 January 2027 [Page 5]
Internet-Draft Protected Authorization July 2026
1.3. Browser Enforcement Precondition
The security properties defined in this document are provided by
browser behavior, not by the header name or value. The header MUST
be inaccessible to page JavaScript, to fetch() and XMLHttpRequest, to
service workers, and to browser extensions (Section 8). *A browser
that cannot enforce all of the protections in Section 8 (for example,
because its extension APIs expose all request headers) MUST NOT
implement this mechanism.* In that case the flow degrades safely to
standard OAuth: no header is generated, and the authorization server
responds with parameters in the redirect URI as it does today.
2. Conventions and Terminology
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
"SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and
"OPTIONAL" in this document are to be interpreted as described in BCP
14 [RFC2119] [RFC8174] when, and only when, they appear in all
capitals, as shown here.
This document uses the terms "authorization request", "authorization
response", "authorization server" (AS), "client", and "redirect URI"
as defined by OAuth 2.0 [RFC6749], and "origin" as defined by
[RFC6454].
*Client*: in this document, "client" always means the OAuth client as
defined by [RFC6749], the application requesting authorization. The
HTTP client performing navigations is always referred to as "the
browser".
*Browser*: a user agent performing top-level navigations on behalf of
a user.
*Redirecting server / redirecting URL*: the server, and the URL, from
which a redirect response was received. The browser attests the
origin (and optionally a path prefix) of the redirecting URL, which
is well defined even when the redirect is the first response in a
navigation (e.g., a login URL that immediately returns a redirect).
*Authorization request and response* always refer to the OAuth
messages [RFC6749], never to HTTP requests and responses. At each
redirect hop, the OAuth-Authorization header field appears in two
HTTP messages: the redirecting server sets it in the redirect
_response_, and the browser delivers it as a header field of the
subsequent HTTP _request_ to the redirect destination. This document
always states which HTTP message is meant.
Hardt & Goto Expires 5 January 2027 [Page 6]
Internet-Draft Protected Authorization July 2026
3. Protocol Overview
The flow is standard OAuth; the additions introduced by this
specification are marked *(new)*:
1. The client constructs a normal authorization request URI and
returns a redirect response (302 or 303) to the browser. *(new)*
The redirect response also carries an OAuth-Authorization header
whose query member is the same serialized query string that
appears in the authorization request URI.
2. *(new)* The browser recognizes the header, adds the browser-
attested origin member (and path if claimed and valid), and
delivers it as a header field of the HTTP request to the
authorization server. The request URI is unchanged.
3. The AS processes the authorization request from the URI as it
does today. *(new)* If the OAuth-Authorization header is present,
the AS verifies the query member matches the request URI query,
and now knows (a) the browser-attested origin of the client and
(b) that the browser and client support protected authorization
responses.
4. The AS authenticates the user and obtains authorization, using
any number of internal redirects or pages; the header plays no
role in these intermediate steps.
5. The AS returns a redirect response to the registered redirect
URI. *(new)* Instead of placing the authorization response
parameters in the redirect URI query, the AS places them in the
query member of an OAuth-Authorization header. The redirect URI
carries no response parameters.
6. *(new)* The browser adds the browser-attested origin of the AS
and delivers the header, exactly once, as a header field of the
HTTP request to the redirect URI.
7. *(new)* The client reads the authorization response parameters
from the header's query member, using the same parsing it uses
for URL query strings today, and verifies the browser-attested
origin is the expected AS.
If any party does not support the mechanism, the header is simply
absent and the flow proceeds as standard OAuth.
4. The OAuth-Authorization Header Field
OAuth-Authorization is a Structured Field Dictionary [RFC9651] with
three members, all Strings: query, origin, and path. The redirecting
server sets the header in a redirect response; the browser validates
and augments it, and delivers it as a header field of the subsequent
HTTP request to the redirect destination (Section 8).
Hardt & Goto Expires 5 January 2027 [Page 7]
Internet-Draft Protected Authorization July 2026
A recipient determines which OAuth message the header carries from
its own role: an authorization endpoint receives authorization
requests; a redirect URI receives authorization responses. Delivery
to the wrong context fails closed (Section 10.3).
4.1. query
Set by the redirecting server; relayed by the browser without
inspection or modification.
The query member carries the OAuth parameters, using the same
serialization as a URL query string, so recipients parse it with the
code they already use for URLs today:
* In an *authorization request* (Section 5), the client sets query
to the query component of the authorization request URI, byte-
identical to it.
* In an *authorization response* (Section 6), the AS sets query to
the serialized authorization response parameters that would
otherwise appear in the redirect URI query.
4.2. origin
Set only by the browser: the ASCII serialization [RFC6454] of the
redirecting URL's origin. Servers MUST NOT set origin; the browser
removes any server-set value before setting its own. A receiving
party can therefore rely on origin unconditionally: it cannot be set,
suppressed, or modified by web content or by any server.
4.3. path
Claimed by the redirecting server; validated, and set or removed, by
the browser.
The redirecting server MAY include a path member as a claim about the
path prefix of the redirecting URL; the value MUST begin and end with
/. The browser delivers the member only if the redirecting URL's path
begins with the claimed value, and removes it otherwise (Section 7).
A receiving party can therefore rely on a delivered path
unconditionally: the browser only delivers claims it has verified.
5. Authorization Request
Hardt & Goto Expires 5 January 2027 [Page 8]
Internet-Draft Protected Authorization July 2026
5.1. Client Behavior
The client constructs the authorization request URI exactly as it
does today; all authorization request parameters remain in the URI
query for compatibility. In the redirect response (302 or 303) that
sends the browser to the authorization server, the client
additionally sets the header:
HTTP/1.1 303 See Other
Location: https://as.example/authorize?client_id=abc&response_type=code
&redirect_uri=https%3A%2F%2Fapp.example%2Fportal%2Fcb&state=123
&code_challenge=E9Melhoa...&code_challenge_method=S256
OAuth-Authorization: query="client_id=abc&response_type=code
&redirect_uri=https%3A%2F%2Fapp.example%2Fportal%2Fcb&state=123
&code_challenge=E9Melhoa...&code_challenge_method=S256",
path="/portal/"
(Line breaks in the examples are for readability only.)
The query member MUST be byte-identical to the query component of the
Location URI. The client MAY include a path claim (Section 7).
The browser processes the header per Section 8 and delivers it with
the navigation:
GET /authorize?client_id=abc&response_type=code&redirect_uri=...
&state=123&code_challenge=...&code_challenge_method=S256 HTTP/1.1
Host: as.example
OAuth-Authorization: query="client_id=abc&response_type=code
&redirect_uri=...&state=123&code_challenge=...
&code_challenge_method=S256",
origin="https://app.example", path="/portal/"
Note that the browser does NOT compare the query member with the
Location URI query. Dropping the header on a mismatch would allow an
attacker able to tamper with the URI to silently downgrade the flow
to unprotected behavior; instead the header is always delivered and
the authorization server detects tampering (Section 5.2).
5.2. Authorization Server Behavior
The AS processes the authorization request from the request URI query
exactly as it does today. Deployments that do not implement this
specification require no changes.
An AS that implements this specification and receives an OAuth-
Authorization header with an authorization request:
Hardt & Goto Expires 5 January 2027 [Page 9]
Internet-Draft Protected Authorization July 2026
1. MUST verify that the query member is byte-identical to the query
component of the request URI, and reject the authorization
request with an error if they differ. A mismatch indicates
tampering or a defective client and MUST NOT be silently ignored.
2. SHOULD verify that the origin member is consistent with the
client's registered redirect_uri values (e.g., that the
registered redirect URIs for the presented client_id belong to
the attested origin), and reject the request otherwise. Unlike
the Referer header, origin is browser-attested: it cannot be set,
suppressed, or modified by web content.
3. MAY use a validated path member to discriminate between multiple
clients registered on the same origin under different path
prefixes.
4. MUST return the authorization response using the OAuth-
Authorization header as defined in Section 6. The header's
arrival with the authorization request is the capability signal:
it can only have arrived via a supporting browser from a
supporting client.
On this leg the header provides origin attestation and tamper
evidence; it does not provide confidentiality. The authorization
request parameters remain visible in the URL, as they are today.
6. Authorization Response
6.1. Authorization Server Behavior
An AS whose authorization request arrived bearing the OAuth-
Authorization header MUST deliver the authorization response in the
header, and MUST NOT include response parameters in the redirect URI:
HTTP/1.1 303 See Other
Location: https://app.example/portal/cb
OAuth-Authorization: query="code=SplxlOBeZQQYbYS6WxSbIA
&state=123&iss=https%3A%2F%2Fas.example"
The AS delivers error responses through the same header, for
consistency: once support is established, every authorization
response, success or error, arrives through the same channel, giving
the client a single processing path. (Keeping error details out of
URLs and logs is a secondary benefit.)
HTTP/1.1 303 See Other
Location: https://app.example/portal/cb
OAuth-Authorization: query="error=access_denied&state=123
&iss=https%3A%2F%2Fas.example"
The AS MAY include a path claim (Section 7).
Hardt & Goto Expires 5 January 2027 [Page 10]
Internet-Draft Protected Authorization July 2026
An AS MUST NOT include an OAuth-Authorization header in its response
redirect unless the corresponding authorization request arrived
bearing the header: without that signal there is no evidence the
browser will deliver the header or that the client will read it.
The browser processes the header per Section 8 and delivers it with
the navigation, exactly once, to the redirect URI only:
GET /portal/cb HTTP/1.1
Host: app.example
OAuth-Authorization: query="code=SplxlOBeZQQYbYS6WxSbIA
&state=123&iss=https%3A%2F%2Fas.example",
origin="https://as.example"
6.2. Client Behavior
A client that receives an OAuth-Authorization header at its redirect
URI:
1. MUST obtain the authorization response parameters by parsing the
query member as a URL query string, and MUST ignore any query
parameters present in the request URI.
2. MUST verify that the origin member matches the origin of the
authorization server it sent the corresponding authorization
request to, and reject the response otherwise. This browser-
attested check complements the iss parameter [RFC9207] and is
stronger: iss is asserted by whichever server sent the response.
3. Processes the parameters (including state verification and the
token request with PKCE) exactly as it does for URL-delivered
responses today.
A client that receives authorization response parameters in the URL
(because the user's browser, or the AS, does not support this
mechanism) processes them as it does today. A client can never
require the header: URL delivery may simply mean an unsupporting
browser (see Section 10.2).
Because the header is delivered only once, clients MUST complete
processing (or persist what they need) on first delivery; a reload of
the redirect URI carries neither the header nor any parameters. Note
that this also means an authorization code can no longer be replayed
out of browser history: there is nothing in the URL or the history
entry to replay.
Hardt & Goto Expires 5 January 2027 [Page 11]
Internet-Draft Protected Authorization July 2026
7. Path Validation
Multiple clients may share an origin, separated by path prefix (e.g.,
https://host.example/app1/ and https://host.example/app2/). The
origin alone cannot discriminate between them. The path member
allows the receiving party to know which path prefix within the
origin the redirect actually came from.
The path member is a claim made by the redirecting server and
validated by the browser:
1. The redirecting server includes path in its header with a value
that MUST begin and end with / (e.g., path="/app1/").
2. The browser checks whether the path component of the redirecting
URL begins with the claimed value.
3. If it does, the browser includes the path member in the delivered
header.
4. If it does not, the browser removes the path member and delivers
the header without it.
The trailing / requirement ensures prefix matching occurs on path-
segment boundaries (/app1/ does not match /app1evil/x).
The redirecting server cannot lie about its path: the browser only
delivers a path claim it has verified. A receiving party that
requires path discrimination treats an absent path member accordingly
(e.g., an AS that registered a client with a path-scoped redirect URI
SHOULD reject a request whose validated path is absent or
inconsistent with the registration).
8. Browser Processing Model
This is fundamentally a browser behavior specification: the security
properties of the OAuth-Authorization header field are created
entirely by the browser processing rules in this section. The
browser acts as a trusted protocol participant, a role it already
plays for every OAuth flow today by enforcing TLS, cookie isolation,
redirect handling, and origin boundaries. This section makes that
role explicit.
*Recognition.* The browser processes the header when, and only when,
it appears in a 302 or 303 response to a top-level navigation. No
URL heuristics are used. The header is ignored (and stripped) in all
other contexts: subresource requests, fetch/XHR responses, embedded
frames, and non-redirect responses.
Hardt & Goto Expires 5 January 2027 [Page 12]
Internet-Draft Protected Authorization July 2026
*Identical processing on both legs.* The browser applies the same
rules whether the redirecting server is a client sending an
authorization request or an AS returning an authorization response.
The browser does not parse or interpret the query member; in
particular, it does not compare it with the Location URI
(Appendix B.8).
*Single hop, stateless.* The header is relayed exactly one hop: from
the redirect response in which the redirecting server set it, to the
HTTP request for the Location URI, and no further. The browser keeps
no state about an OAuth flow across hops; if an intermediate
destination redirects again, that response must itself set the header
for the browser to relay it. Any transient state used to relay the
header is destroyed after delivery.
*Browser-generated members.* The browser removes any origin member
set by a server and sets it itself; the browser validates any server-
claimed path member and delivers it only if valid. Receiving parties
can therefore rely on origin and path unconditionally.
*Protection requirements.* The browser MUST ensure that:
* Web content cannot set this header field on any request: it MUST
be treated as a forbidden header name in the sense of Fetch
[FETCH].
* JavaScript cannot read this header field from any request or
response, including via fetch(), XMLHttpRequest, performance and
reporting APIs, or any other API.
* Service workers cannot observe or modify this header field, on
either the redirect responses that carry it or the requests that
deliver it, including via navigation preload.
* Browser extensions cannot read, modify, inject, suppress, or
replay this header field, regardless of the permissions the
extension holds.
* The header field is processed only for top-level navigations,
delivered only to the Location destination of the redirect that
carried it, delivered at most once, and never persisted.
These protections are intentionally stronger than those of Sec--
prefixed header fields, which today remain readable by extensions and
service workers. A browser that cannot enforce every requirement in
this list MUST NOT implement this mechanism (see Section 10.1).
Hardt & Goto Expires 5 January 2027 [Page 13]
Internet-Draft Protected Authorization July 2026
*Standards coordination.* The processing model in this section (the
forbidden header name, service worker opacity, and navigation
integration) requires a normative change to the WHATWG Fetch standard
[FETCH]. A Fetch pull request defining this processing model is a
deliverable of this work; this document defines the OAuth protocol
semantics that rely on it.
9. Deployment Considerations
9.1. Incremental Adoption
No coordination between parties is required. Each party adds support
independently, in any order:
* *Clients* add the OAuth-Authorization header to the redirect
responses they already send, keeping all parameters in the URL,
and read authorization responses from the header when present,
falling back to URL parameters when it is not. This is a library
update.
* *Browsers* implement the processing model in Section 8.
* *Authorization servers* verify and use the header when it arrives
with an authorization request, and switch the authorization
response to the header, whose arrival proves the whole path
supports it.
Until all three parties support the mechanism, every flow proceeds
exactly as standard OAuth. There is no flag day and no breakage for
any non-supporting party.
9.2. Dual Transmission of Request Parameters
Clients send the authorization request parameters in both the URL and
the header on every request, indefinitely: a client cannot know
whether any given user's browser supports this mechanism, and this
specification deliberately defines no discovery mechanism. This is
why the authorization request gains security but not privacy
(Section 11.1), and why the authorization response, which is sent in
the header only when support is proven, gains both.
9.3. Single-Page Applications
The authorization response is delivered to the server at the redirect
URI; it is, by design, not readable by JavaScript (Appendix B.11).
SPAs that process authorization codes in front-end code continue to
work exactly as they do today (they will not receive the header
because they do not send it from a server-issued redirect response).
SPAs that adopt a backend-for-frontend gain the protections of this
specification.
Hardt & Goto Expires 5 January 2027 [Page 14]
Internet-Draft Protected Authorization July 2026
9.4. Header Sizes
Authorization code responses are small (typically well under 1 KB).
Deployments that layer large response payloads into the query member,
such as JARM [JARM] response JWTs, should be aware of intermediary
header size limits, commonly 8 to 16 KB.
10. Security Considerations
10.1. Dependence on Browser Enforcement
Everything in this specification depends on the browser protections
in Section 8. If extensions, service workers, or page script can
observe or forge the header, the mechanism provides no security
benefit over URL parameters and MUST NOT be implemented. This is the
central deployment requirement of this specification and is
intentionally stated bluntly: the security properties are defined by
browser behavior, and exist only where the browser provides them.
This specification assumes an honest, conforming browser. It cannot
protect against a compromised or malicious browser, browser bugs that
fail to enforce the processing model, or debugging tools operating
with the user's authority. Servers SHOULD treat anomalous origin
values as potential indicators of a non-conforming implementation.
10.2. Downgrade
An attacker who prevents the mechanism from operating (for example,
by interfering with a non-protected portion of the flow) obtains at
most today's OAuth: parameters in URLs, with all [RFC9700]
mitigations (PKCE, state, one-time short-lived codes, exact redirect
URI matching) still in force. A client cannot distinguish "the
user's browser does not support this" from "support was stripped",
and therefore can never hard-require the header; this residual
downgrade is accepted and is exactly the status quo. The browser is
stateless across the flow (Section 8), so it cannot mark a callback
as "should have been protected"; a future extension could revisit
this if browsers ever maintain per-flow state.
Query tampering, by contrast, is not a downgrade vector: the browser
always delivers the header with the authorization request, and the AS
rejects on mismatch (Section 5.2).
10.3. Cross-Context Delivery
A single header field name means a header could in principle reach a
party expecting the other OAuth message. Both directions fail
closed:
Hardt & Goto Expires 5 January 2027 [Page 15]
Internet-Draft Protected Authorization July 2026
* An authorization endpoint that receives a header whose query
member is not byte-identical to the request URI query rejects the
request (Section 5.2); a header carrying authorization response
parameters never matches an authorization request URI.
* A client that receives a header at its redirect URI verifies the
browser-attested origin is its expected AS (Section 6). A header
minted by any other party, including an attacker's site issuing a
redirect to the client's redirect URI, carries the attacker's
origin and is rejected. Web content and extensions cannot forge
the header at all (Section 8).
10.4. What Is and Is Not Protected
Protected: the authorization code, and the other authorization
response parameters, never appear in URLs, eliminating exposure via
browser history, web server and proxy logs, Referer headers,
analytics and crash reporting, URL sharing, screenshots, and copy/
paste; and both parties receive a browser-attested origin for the
other side of each redirect.
Not protected: parameters in transit (TLS remains REQUIRED for every
HTTP request and response carrying the header); the authorization
request parameters, which remain in the URL by design; the parties
themselves (a malicious AS or client is out of scope); and injection
of an attacker's _legitimate_ authorization response at a client
(authorization code injection), for which PKCE remains REQUIRED.
10.5. Server-Side Handling
When carrying an authorization response, the query member contains
the authorization code and MUST be treated with the confidentiality
of an Authorization header: excluded or redacted in access logs,
application logs, and telemetry. Because the same field name is used
on both legs, the simple deployment rule is to treat OAuth-
Authorization as sensitive everywhere. Moving parameters out of URLs
removes them from _default_ URL logging; header logging is a
configuration choice that servers MUST make deliberately.
Receiving parties MUST parse the header as a Structured Field
[RFC9651] and reject malformed values. An HTTP request or response
carrying more than one instance of the header field is invalid;
recipients MUST ignore the header field entirely in that case, and
browsers MUST NOT relay duplicated instances.
Hardt & Goto Expires 5 January 2027 [Page 16]
Internet-Draft Protected Authorization July 2026
10.6. Relationship to Existing Mechanisms
This mechanism supplements and does not relax any existing
requirement: redirect URI registration and exact matching, state (or
equivalent CSRF protection), PKCE [RFC7636], and the mitigations of
[RFC9700] all continue to apply. The origin member provides a
browser-attested complement to iss [RFC9207] for mix-up defense, and
browser-attested client origin strengthens the AS's ability to detect
requests initiated from unexpected origins, supplementing rather than
replacing redirect URI validation.
11. Privacy Considerations
11.1. Authorization Request Privacy
Authorization request parameters remain in the URL indefinitely
(Section 9) and remain exposed exactly as they are today. This is
explicitly a non-goal: authorization request parameters (client_id,
redirect_uri, state, code_challenge) are not secrets. Deployments
that need request confidentiality should use PAR [RFC9126].
11.2. Authorization Response Privacy
Authorization response parameters never appear in URLs, browser
history, Referer headers, or default logs, and the header is
invisible to page JavaScript, embedded third parties, service
workers, and extensions. The header is delivered once and never
persisted.
11.3. No New Cross-Site Information Flow
This mechanism activates only on OAuth authorization navigations:
flows that by design already convey the client's identity to the AS
(client_id, redirect_uri) in the URL. The browser-attested origin
gives the AS nothing it does not already receive; it makes an
existing claim reliable rather than adding a new one. On the return
leg, the AS origin delivered to the client identifies a party the
client chose and already knows. The header is invisible to all third
parties, so it cannot be used as a side channel between origins.
What remains true, and is unchanged by this specification, is that
front-channel OAuth inherently reveals to the AS that a user is
authorizing a given client; only back-channel protocols such as
[I-D.hardt-oauth-aauth-protocol] change that.
Hardt & Goto Expires 5 January 2027 [Page 17]
Internet-Draft Protected Authorization July 2026
11.4. Origin and Path Disclosure
The origin member is a reliable equivalent of a scheme-plus-host
Referer, and a validated path member discloses a path prefix, but
only to the party the redirect was addressed to, which in OAuth
already knows the counterparty. Unlike Referer, these members cannot
be stripped by the user without the flow degrading to URL parameters
(which disclose strictly more). This trade-off, favoring reliable
counterparty verification over origin hiding, is appropriate only
where mutual knowledge of the parties is expected, as it is in OAuth;
it is one of the reasons this mechanism is OAuth-specific rather than
generic (Appendix B.1).
11.5. User Transparency
Users cannot inspect or modify the header in-page (unlike URL
parameters). Browsers SHOULD surface it in developer tools, read-
only, while maintaining all protections in Section 8.
12. IANA Considerations
This document registers one header field in the "Hypertext Transfer
Protocol (HTTP) Field Name Registry" defined in [RFC9110].
12.1. OAuth-Authorization Header Field
Field name: OAuth-Authorization
Status: permanent
Structured Type: Dictionary
Reference: [this document]
13. Implementation Status
*Note to RFC Editor: Please remove this section before publication.*
Specification status: Exploratory draft. This document replaces
draft-hardt-httpbis-redirect-headers, refocused on the OAuth use case
following IETF 125 feedback.
Browser support: not yet implemented; requires the Fetch processing
model in Section 8.
Server and client support: reference implementations needed; adoption
is a library update for both.
Hardt & Goto Expires 5 January 2027 [Page 18]
Internet-Draft Protected Authorization July 2026
14. Acknowledgments
The authors would like to thank early reviewers for their valuable
feedback and insights that helped shape this proposal: Jonas Primbs,
Warren Parad. This document was refocused on the OAuth use case in
response to feedback from the HTTPBIS working group at IETF 125, in
particular from Martin Thomson, Justin Richer, David Waite, Mike
Bishop, Li Ruochen, and Yaroslav Rosomakho.
15. References
15.1. Normative References
[RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
Requirement Levels", BCP 14, RFC 2119,
DOI 10.17487/RFC2119, March 1997,
<https://www.rfc-editor.org/info/rfc2119>.
[RFC6454] Barth, A., "The Web Origin Concept", RFC 6454,
DOI 10.17487/RFC6454, December 2011,
<https://www.rfc-editor.org/info/rfc6454>.
[RFC6749] Hardt, D., Ed., "The OAuth 2.0 Authorization Framework",
RFC 6749, DOI 10.17487/RFC6749, October 2012,
<https://www.rfc-editor.org/info/rfc6749>.
[RFC8174] Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC
2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174,
May 2017, <https://www.rfc-editor.org/info/rfc8174>.
[RFC9110] Fielding, R., Ed., Nottingham, M., Ed., and J. Reschke,
Ed., "HTTP Semantics", STD 97, RFC 9110,
DOI 10.17487/RFC9110, June 2022,
<https://www.rfc-editor.org/info/rfc9110>.
[RFC9651] Nottingham, M. and P. Kamp, "Structured Field Values for
HTTP", RFC 9651, DOI 10.17487/RFC9651, September 2024,
<https://www.rfc-editor.org/info/rfc9651>.
[RFC9700] Lodderstedt, T., Bradley, J., Labunets, A., and D. Fett,
"Best Current Practice for OAuth 2.0 Security", BCP 240,
RFC 9700, DOI 10.17487/RFC9700, January 2025,
<https://www.rfc-editor.org/info/rfc9700>.
15.2. Informative References
[FETCH] WHATWG, "Fetch Living Standard", 2026,
<https://fetch.spec.whatwg.org/>.
Hardt & Goto Expires 5 January 2027 [Page 19]
Internet-Draft Protected Authorization July 2026
[I-D.hardt-oauth-aauth-protocol]
Hardt, D., "AAuth Protocol", Work in Progress, Internet-
Draft, draft-hardt-oauth-aauth-protocol-08, 24 June 2026,
<https://datatracker.ietf.org/doc/html/draft-hardt-oauth-
aauth-protocol-08>.
[JARM] Lodderstedt, T. and B. Campbell, "JWT Secured
Authorization Response Mode for OAuth 2.0 (JARM)",
November 2022,
<https://openid.net/specs/oauth-v2-jarm.html>.
[RFC7636] Sakimura, N., Ed., Bradley, J., and N. Agarwal, "Proof Key
for Code Exchange by OAuth Public Clients", RFC 7636,
DOI 10.17487/RFC7636, September 2015,
<https://www.rfc-editor.org/info/rfc7636>.
[RFC8252] Denniss, W. and J. Bradley, "OAuth 2.0 for Native Apps",
BCP 212, RFC 8252, DOI 10.17487/RFC8252, October 2017,
<https://www.rfc-editor.org/info/rfc8252>.
[RFC9126] Lodderstedt, T., Campbell, B., Sakimura, N., Tonge, D.,
and F. Skokan, "OAuth 2.0 Pushed Authorization Requests",
RFC 9126, DOI 10.17487/RFC9126, September 2021,
<https://www.rfc-editor.org/info/rfc9126>.
[RFC9207] Meyer zu Selhausen, K. and D. Fett, "OAuth 2.0
Authorization Server Issuer Identification", RFC 9207,
DOI 10.17487/RFC9207, March 2022,
<https://www.rfc-editor.org/info/rfc9207>.
Appendix A. Example: Authorization Code Flow Before and After
A.1. Without This Mechanism (Current OAuth)
Client redirects to the AS:
HTTP/1.1 303 See Other
Location: https://as.example/authorize?client_id=abc&response_type=code
&redirect_uri=https%3A%2F%2Fapp.example%2Fportal%2Fcb&state=123
&code_challenge=E9Melhoa...&code_challenge_method=S256
The AS has only the unreliable Referer header to know where the user
came from. After authorization, the AS redirects back:
HTTP/1.1 303 See Other
Location: https://app.example/portal/cb?code=SplxlOBeZQQYbYS6WxSbIA
&state=123&iss=https%3A%2F%2Fas.example
Hardt & Goto Expires 5 January 2027 [Page 20]
Internet-Draft Protected Authorization July 2026
The authorization code is now in the URL: recorded in browser
history, in the client's access logs and any intermediary's logs,
sent in the Referer header to any third-party resource the callback
page loads, and available to be shared, screenshotted, or pasted.
A.2. With This Mechanism
Client redirects to the AS; the URL is identical to today, plus the
header:
HTTP/1.1 303 See Other
Location: https://as.example/authorize?client_id=abc&response_type=code
&redirect_uri=https%3A%2F%2Fapp.example%2Fportal%2Fcb&state=123
&code_challenge=E9Melhoa...&code_challenge_method=S256
OAuth-Authorization: query="client_id=abc&response_type=code
&redirect_uri=https%3A%2F%2Fapp.example%2Fportal%2Fcb&state=123
&code_challenge=E9Melhoa...&code_challenge_method=S256",
path="/portal/"
The redirecting URL is https://app.example/portal/login, so the
browser validates the /portal/ path claim, attests the origin, and
delivers:
GET /authorize?client_id=abc&response_type=code&redirect_uri=...
&state=123&code_challenge=...&code_challenge_method=S256 HTTP/1.1
Host: as.example
OAuth-Authorization: query="client_id=abc&response_type=code
&redirect_uri=...&state=123&code_challenge=...
&code_challenge_method=S256",
origin="https://app.example", path="/portal/"
The AS verifies the query member matches the URL, verifies
https://app.example is consistent with the registered redirect URI
for client_id=abc, and, knowing the browser and client support
protected authorization responses, returns the response in the header
with a clean redirect URI:
HTTP/1.1 303 See Other
Location: https://app.example/portal/cb
OAuth-Authorization: query="code=SplxlOBeZQQYbYS6WxSbIA
&state=123&iss=https%3A%2F%2Fas.example"
The browser attests the AS origin and delivers, once:
Hardt & Goto Expires 5 January 2027 [Page 21]
Internet-Draft Protected Authorization July 2026
GET /portal/cb HTTP/1.1
Host: app.example
OAuth-Authorization: query="code=SplxlOBeZQQYbYS6WxSbIA
&state=123&iss=https%3A%2F%2Fas.example",
origin="https://as.example"
The client verifies origin is its expected AS, parses the query
member with its existing query-string parser, verifies state, and
exchanges the code (with its PKCE verifier). The authorization code
never appeared in any URL: nothing in browser history, nothing in
URL-based logs, nothing in Referer headers, nothing to share or
replay.
Appendix B. Design Rationale
B.1. Why OAuth-Specific Rather Than Generic Redirect Headers
A generic redirect-header mechanism gives the browser no way to know
when protections apply, no defined trust model, and a broad new
surface for navigation tracking. Scoping to OAuth lets the browser
recognize exactly one flow shape, attach exactly the metadata that
flow needs, and constrain the mechanism to navigations that already
carry cross-site identifiers by design.
B.2. Why a Single Header Field
The browser's processing is identical on both legs of the flow, so
one header field name means one processing rule, one forbidden header
name in Fetch, and one IANA registration. Separate "request" and
"response" names would also invite conflating the OAuth message
direction with the HTTP message direction: the header appears in an
HTTP response and then an HTTP request on _each_ leg. The receiving
endpoint's role already determines which OAuth message the header
carries, and delivery to the wrong context fails closed
(Section 10.3).
B.3. Why Not Cryptographic Protection of Parameters
The exposure this document addresses is at the endpoints, not on the
channel: TLS already protects parameters in transit. An encrypted or
signed response (JARM [JARM]) placed in a URL is still a URL: still
written to history, still in access logs, still sent in Referer
headers, still shareable. Cryptographic protection also requires key
distribution between every client and AS pair, and no cryptographic
scheme can attest a client's _web origin_: only the user agent knows
which origin actually initiated a navigation. The mechanisms
compose: a JARM response can be carried in the header's query member,
gaining URL-freedom on top of its integrity properties.
Hardt & Goto Expires 5 January 2027 [Page 22]
Internet-Draft Protected Authorization July 2026
B.4. Why Not Move to the Back Channel
Not putting sensitive parameters in the front channel at all is the
strongest protection, and it is a different protocol. PAR [RFC9126]
moves the authorization request to the back channel; the AAuth
protocol [I-D.hardt-oauth-aauth-protocol] moves the entire
authorization exchange to authenticated back-channel HTTP. Those
approaches carry different deployment costs: new endpoints, client
authentication, and protocol changes on both sides. This
specification exists for the enormous installed base of redirect-
based OAuth deployments, which can adopt it as a library update with
no new endpoints, no keys, and no change to how authorization
parameters are constructed or parsed.
B.5. Why the Authorization Request Parameters Stay in the URL
Compatibility, permanently. The authorization request URI is
processed by every existing AS with no changes, and the client can
never know whether the user's browser supports this mechanism, so it
always sends both (Section 9). The query member on this leg is not
for confidentiality (authorization request parameters are not
secrets) but provides a canonical copy bound to browser-attested
origin metadata, a tamper check (Section 5.2), and the capability
signal that unlocks the protected authorization response.
B.6. Why a Structured Field Dictionary Wrapping a Query String
The outer Structured Field [RFC9651] layer gives the browser a well-
defined, extensible place for the members it owns (origin, path)
without colliding with the OAuth parameter namespace. The OAuth
parameters themselves stay query-string encoded inside the query
member, so every implementation parses them with the same code path
it uses for URLs today: no new parameter encoding, and no dual-parser
inconsistency bugs.
B.7. Why Redirect Responses Only (Why Not form_post)
This mechanism applies exclusively to redirect responses. The
form_post response mode places parameters in the document as form
fields, where they are visible to page JavaScript and extensions no
matter what the browser does with headers; there is nothing there for
the browser to protect. Redirects are also what the overwhelming
majority of deployments use, and extending them is a library change
rather than an infrastructure change.
Hardt & Goto Expires 5 January 2027 [Page 23]
Internet-Draft Protected Authorization July 2026
B.8. Why the AS Rejects on Query Mismatch, Rather Than the Browser
Dropping the Header
If the browser dropped the header when the query member disagreed
with the URL, an attacker able to modify the URL query could make the
header vanish, silently downgrading the flow to unprotected URL
parameters. Instead the browser always delivers the header, and the
AS treats a mismatch as an error (Section 5.2). Tampering becomes a
visible failure rather than a silent downgrade.
B.9. Why Not a Sec-Prefixed Name
The Sec- prefix means only that web content cannot _set_ a header;
extensions and service workers can still read Sec- request headers
today. The protections required here are strictly stronger
(Section 8) and are defined by normative browser behavior plus
forbidden-header-name registration in Fetch. A Sec- prefix would
understate the guarantee being made, and would break the symmetry of
the same header field flowing from a redirect response into the
browser's subsequent HTTP request.
B.10. Why Not a response_mode Value
The protected authorization response is functionally a new response
mode, but it is negotiated by demonstrated browser capability (the
header's arrival with the authorization request) rather than
requested by the client. A response_mode parameter would allow a
client to request behavior the user's browser may not support, which
cannot work. In-band capability signaling is the only reliable
negotiation.
B.11. Why the Authorization Response Is Not Readable by JavaScript
Deliberate. [RFC9700] and current browser-based-app guidance steer
authorization codes away from script-accessible surfaces, where they
are exposed to XSS, injected third-party code, and extensions.
Delivering the authorization response only to the server at the
redirect URI enforces that guidance structurally.
B.12. Why Native Application Flows Are Out of Scope
The header cannot survive the custom-scheme or app-link handoff from
the system browser back to a native app: the operating system
delivers a URL, not headers. The threat model also differs:
dedicated system authentication sessions do not run page JavaScript
or extensions in the same way. Native flows continue to use their
existing mechanisms (with PKCE, per [RFC8252]).
Hardt & Goto Expires 5 January 2027 [Page 24]
Internet-Draft Protected Authorization July 2026
B.13. Why Browser-Attested Origin When iss Exists
The iss parameter [RFC9207] is asserted by whichever server sends the
response; in the mix-up scenarios it targets, the client must trust
the asserter. The origin member is attested by the browser and
cannot be set or influenced by any server. The two compose: iss
travels unchanged inside the query member, and the client
additionally gets an unforgeable statement of which origin the
response actually came from.
Authors' Addresses
Dick Hardt
Hellō
Email: dick.hardt@gmail.com
Sam Goto
Google
Email: goto@google.com
Hardt & Goto Expires 5 January 2027 [Page 25]