
Understanding OAuth 2.0 Pushed Authorization Requests (PAR)
An examination of RFC 9126 and how Pushed Authorization Requests enhance OAuth 2.0 security by preventing authorization code interception attacks.
The OAuth 2.0 ecosystem relies on trust between clients, authorization servers, and users. In standard flows, the client redirects the browser to the authorization endpoint with a URL containing sensitive parameters like client IDs, scopes, and state tokens. This exposes the full authorization intent to public networks. If an attacker intercepts this URL, they can replay it to steal the authorization code, leading to a successful interception attack.
RFC 9126 introduces Pushed Authorization Requests (PAR) to sever the link between sensitive parameters and the public redirect URL. Instead of sending the full parameter set to the browser, the client sends a POST request directly to the Authorization Server's PAR endpoint. The server validates these parameters, stores them temporarily, and returns a unique request_uri. The client then redirects the browser to the authorization endpoint, but the query string now contains only this short request_uri and the state parameter. The server retrieves the full request context from its internal store using the request_uri.
Consider a concrete scenario involving a client named finance-app, an authorization server auth.example.com, and a user named Alice. In the legacy flow without PAR, finance-app constructs a URL like:
https://auth.example.com/oauth2/authorize?
client_id=finance-app&
redirect_uri=https://finance-app.com/callback&
scope=read:transactions&
state=xyz123&
code_challenge=S256&
code_challenge_method=S256
This URL is long, complex, and often gets logged by web servers, load balancers, or browser history extensions. If a man-in-the-middle captures this URL, they possess everything needed to trick Alice into granting access.
With PAR enabled, finance-app first constructs a JSON payload containing those exact parameters and sends a POST request to https://auth.example.com/oauth2/par.
POST /oauth2/par HTTP/1.1
Host: auth.example.com
Content-Type: application/json
{
"client_id": "finance-app",
"redirect_uri": "https://finance-app.com/callback",
"scope": "read:transactions",
"state": "xyz123",
"code_challenge": "S256",
"code_challenge_method": "S256"
}If the Authorization Server accepts the request, it responds with a 201 Created status and a request_uri in the body.
HTTP/1.1 201 Created
Content-Type: application/json
{
"request_uri": "urn:ietf:params:oauth:request_uri:8f7d6e5c4b3a2910",
"expires_in": 600
}Now, when finance-app redirects Alice's browser, the URL is drastically shortened:
https://auth.example.com/oauth2/authorize?
request_uri=urn:ietf:params:oauth:request_uri:8f7d6e5c4b3a2910&
state=xyz123
The sensitive parameters are no longer in the URL. They exist only in the Authorization Server's memory or database, bound to that specific request_uri and the client's identity. If an attacker intercepts the redirect URL, they only see the request_uri. Without the prior POST request context, the request_uri is just a random string. The server will reject any attempt to use this request_uri unless it was issued in a valid, authenticated POST request from the legitimate client.
This mechanism fundamentally changes the trust model. The security of the request parameters now relies on the authentication of the POST request to the PAR endpoint, rather than the confidentiality of the URL itself. This is critical because the POST request uses the client's credentials (like a client secret or private key) which are never exposed to the browser or the public network in the same way a URL is. The request_uri itself is a reference token, not the data payload.
The implementation of PAR requires the Authorization Server to enforce strict usage policies. When the authorization endpoint receives a request_uri, it must verify that the request matches the one stored at the PAR endpoint. It must also ensure the request_uri has not expired. RFC 9126 mandates that the request_uri must be invalidated immediately after use. This prevents replay attacks where an attacker might try to use a captured request_uri multiple times.
There is a tradeoff here. PAR adds an extra network round-trip between the client and the authorization server before the user even sees the login page. For high-latency networks or mobile clients with poor connectivity, this adds a few hundred milliseconds to the login time. However, the security gain is substantial. It mitigates the risk of URL-based leakage, which is a common failure mode in complex enterprise environments where logging infrastructure is aggressive.
Furthermore, PAR and PKCE are independent mechanisms. PKCE protects against interception attacks by ensuring only the original client can exchange the code, while PAR protects parameters from leakage by keeping them off the URL. They are complementary; neither mandates the other, though using both provides defense in depth. More importantly, PAR is essential for securing the OAuth 2.1 specification, which aims to simplify and harden the protocol. By pushing the parameters to the server, you reduce the surface area for parameter tampering in transit.
The request_uri format is not arbitrary. RFC 9126 specifies a URN format to distinguish it from standard URLs. The server must treat the request_uri as opaque data. It cannot assume the structure of the data within the request_uri beyond the fact that it points to a stored request. This abstraction allows the Authorization Server to implement its own storage mechanisms, whether that is in-memory caching for speed or database persistence for durability.
In summary, PAR shifts the burden of parameter integrity from the URL transport layer to the application layer authentication. By forcing the client to authenticate the request parameters before generating the redirect, PAR ensures that even if the redirect URL is stolen, the thief cannot reconstruct the original authorization intent. This is the core mechanism by which PAR defends against authorization code interception attacks. Successful PAR implementation requires careful configuration of both client and server components.
Conclusion
Pushed Authorization Requests (PAR) represent a significant evolution in OAuth 2.0 security practices. By decoupling the sensitive authorization parameters from the browser redirect, PAR effectively neutralizes the authorization code interception attack vector that plagues standard flows. While it introduces a minor latency cost, the trade-off is overwhelmingly in favor of security, particularly in environments where URL logging and proxy visibility are unavoidable. With OAuth 2.1 adoption accelerating, PAR is becoming a necessity for robust identity management systems.
FAQ
How does PAR differ from standard flows?
In standard flows, all authorization parameters are passed in the URL query string, making them visible to logs and proxies. PAR moves these parameters to a server-side store via a POST request, passing only a short reference token (request_uri) in the URL.
Does PAR work with PKCE? Yes, PAR and PKCE are independent but complementary. PAR secures the parameters from leakage, while PKCE secures the code exchange against interception. They are often used together for maximum security.
What are the performance implications? PAR introduces one additional network round-trip (Client to Server and back) before the user interaction begins. This typically adds a few hundred milliseconds to the login process, which is generally acceptable given the significant security improvements.
Common Pitfalls
- Reusing
request_uri: Attempting to reuse arequest_uriafter it has been consumed or expired will result in errors. The spec requires immediate invalidation after use. - Incorrect
Content-Type: Sending the PAR request withapplication/x-www-form-urlencodedinstead ofapplication/jsonwill cause the authorization server to reject the request, as RFC 9126 specifies JSON. - Failing to invalidate
request_uri: If the server does not invalidate therequest_uriimmediately after the code is issued, attackers could potentially replay the reference token.
Practical Takeaways
- Always use
application/jsonfor the POST request body to the PAR endpoint. - Ensure your Authorization Server strictly enforces the "use once and invalidate" rule for
request_uri. - Monitor logs for failed PAR requests to detect potential reconnaissance or attack attempts early.
Related posts
Angular OAuth2/OIDC: loadDiscoveryDocumentAndTryLogin
Learn how to use loadDiscoveryDocumentAndTryLogin and strict discovery document validation in Angular for secure OAuth2/OIDC authentication.
The AuthConfig Reference: Every Property That Matters
A complete reference for Angular-OAuth2-OIDC AuthConfig properties, covering requireHttps, remoteOnly, and nonceStateSeparator for secure Angular authentication.
Implementing and Validating Discovery in Your Client
A technical walkthrough for backend developers on implementing OAuth 2.1 discovery, issuer validation, and strict discovery document validation using OpenIDConnectConfigurationRetriever.