| Internet-Draft | Protected Authorization | July 2026 |
| Hardt & Goto | Expires 5 January 2027 | [Page] |
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.¶
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.¶
This Internet-Draft is submitted in full conformance with the provisions of BCP 78 and BCP 79.¶
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 (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.¶
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:¶
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).¶
This mechanism complements PKCE [RFC7636], PAR [RFC9126], iss identification [RFC9207], and JARM [JARM]. It does not replace any of them.¶
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.¶
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.¶
The flow is standard OAuth; the additions introduced by this specification are marked (new):¶
OAuth-Authorization header whose query member is the same serialized query string that appears in the authorization request URI.¶
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.¶
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.¶
query member of an OAuth-Authorization header. The redirect URI carries no response parameters.¶
origin of the AS and delivers the header, exactly once, as a header field of the HTTP request to the redirect URI.¶
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.¶
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).¶
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).¶
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:¶
query to the query component of the authorization request URI, byte-identical to it.¶
query to the serialized authorization response parameters that would otherwise appear in the redirect URI query.¶
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.¶
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.¶
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:¶
path in its header with a value that MUST begin and end with / (e.g., path="/app1/").¶
path member in the delivered header.¶
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).¶
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.¶
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:¶
fetch(), XMLHttpRequest, performance and reporting APIs, or any other API.¶
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).¶
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.¶
No coordination between parties is required. Each party adds support independently, in any order:¶
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.¶
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.¶
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.¶
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.¶
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.¶
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.¶
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).¶
A single header field name means a header could in principle reach a party expecting the other OAuth message. Both directions fail closed:¶
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.¶
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).¶
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.¶
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.¶
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.¶
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].¶
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.¶
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).¶
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.¶
This document registers one header field in the "Hypertext Transfer Protocol (HTTP) Field Name Registry" defined in [RFC9110].¶
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.¶
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.¶
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.¶
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).¶
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.¶
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.¶
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.¶
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.¶
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.¶
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.¶
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.¶
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.¶
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]).¶
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.¶