In highly sensitive contexts, such as in financial or healthcare sectors, the security bar must be raised to prevent any breach of information and artifacts that could give unauthorized access to user data. Pushed Authorization Requests (PAR) is one extension that enhances OAuth 2.0 and OpenID Connect so that they can be securely used in these scenarios. Let's explore how this OAuth 2.0 extension works.
Why use PAR?
Let's start by first understanding why you may need PAR and what problem it tries to resolve.
The OAuth authorization request
If you have some experience with OAuth 2.0 and OpenID Connect, you know that the initial step that a client application must take to get the tokens it needs is to send an authorization request to the authorization server. For this purpose, the user's browser is redirected to the authorization server with a URL similar to the following:
https://your-authorization-server.com/authorize?
client_id=1234567890&
redirect_uri=https://your-app/callback&
scope=openid profile email phone address&
response_type=code&
audience=https://myapi&
state=12c1d32c1aba2cc14f9b907a9ec90
This authorization request URL is pretty basic, but you can add other parameters based on the OAuth flow you are using or on the specific extension/customization your authorization server implements. For example, if you are using the Authorization Code Flow with PKCE, you should also add the code_challenge
and code_challenge_method
parameters. In Auth0, you can also specify the organization
or invitation
parameters if you are using the Auth0 Organizations feature, and so on. The complexity of the authorization request can grow considerably depending on your specific use case.
What's the problem with the authorization request?
As you can see, the authorization request is nothing but a GET HTTP request against a URL with its parameters:
GET /authorize?
client_id=1234567890&
redirect_uri=https%3A%2F%2Fyour-app%2Fcallback&
scope=openid%20profile%20email%20phone%20address&
response_type=code&
audience=https%3A%2F%2Fmyapi&
state=12c1d32c1aba2cc14f9b907a9ec90 HTTP/1.1
Host: your-authorization-server.com
In this form, it may raise a few concerns:
- The authorization request can be altered. Usually, the client application redirects the user's browser to the authorization server with the authorization URL. Nothing prevents a malicious actor from changing one or more parameters of this request before sending it to the authorization server.
- No guarantee of the request's provenance. The authorization request is normally built by the client application, and the user's browser is in charge of sending it to the authorization server. However, there is no guarantee that the request has been built by the client application. Anyone can build an authorization request once they know a few data, such as the client ID and the redirect URI.
- No guarantee of confidentiality. Although the browser sends the authorization request via HTTPS, the request parameters can be intercepted by third-party applications, such as a proxy, a load balancer, or even a browser plugin. A malicious network component of this type can inject or change the request parameters, not to mention that the request itself can be logged.
- Browser limitations. Finally, a very complex query string in the authorization request may incur possible browser limitations on URL length.
This is where Pushed Authorization Requests come into play.
How PAR Works
The basic idea of Pushed Authorization Requests is very simple: instead of redirecting the user's browser to make the authorization request with all the parameters, the client application pushes that request directly to the authorization server with a POST request and gets an identifier for that request. Now, the client application redirects the user's browser to the authorization server with just that identifier to make its authorization request, avoiding exposing all the details.
The PAR endpoint
The first requirement for an authorization server supporting PAR is to expose the PAR endpoint. This is an additional endpoint that will accept authorization requests with the parameters in the payload. The following example shows how a request to the PAR endpoint looks like:
POST /oauth/par HTTP/1.1
Host: your-authorization-server.com
Content-Type: application/x-www-form-urlencoded
client_id=1234567890&
client_secret=<YOUR_SECRET>&
redirect_uri=https%3A%2F%2Fyour-app%2Fcallback&
scope=openid%20profile%20email%20phone%20address&
response_type=code&
audience=https%3A%2F%2Fmyapi
Notice the reference to the /oauth/par
path and how the parameters are passed through POST. You can see some differences between the parameters passed in the standard GET-based authorization request and this request. Apart from the usual parameters such as client_id
, redirect_uri
, scope
, etc., you may notice that the client_secret
is passed here. This is a request coming from a confidential client, that is, a client capable of securely storing a secret, such as a server-running application. By passing client_id
and client_secret
, the request is authenticated by the authorization server resolving the confidentiality concerns we discussed earlier.
The PAR specification does not limit access to the PAR endpoint to confidential clients only. It states that "the rules for client authentication as defined in [RFC6749] for token endpoint requests, including the applicable authentication methods, apply for the PAR endpoint as well."
This entails that even public clients can potentially access the PAR endpoint. However, their public nature does not allow them to safely determine their identity, so some of the concerns about the integrity and confidentiality of the request still remain in this case.
As a response to this request, the authorization server will respond with something like the following:
HTTP/1.1 201 Created
Content-Type: application/json
{
"request_uri": "urn:ietf:params:oauth:request_uri:6esc_11ACi5bwq064ltcg4eY28s",
"expires_in": 30
}
The authorization server's response is a JSON object with two properties:
request_uri
. This is the identifier of the authorization request pushed by the client application.expires_in
. This is the number of seconds that the authorization request is valid.
With these basic notions, let's take a look at how the whole authorization process works.
The PAR authorization flow
The following diagram illustrates the OAuth authorization flow using PAR:
Here are the steps highlighted in the diagram:
The client application sends an authorization request to the PAR endpoint of the authorization server.
The authorization server authenticates the application client, validates the request, and stores it. Then, it replies with a response containing the authorization request's identifier (
request_uri
) and the validity time (expires_in
).The client application uses the
request_uri
to build the authorization request URL for the user's browser and redirects it to the authorization server. The authorization request URL will look like the following:https://your-authorization-server.com/authorize? client_id=1234567890& request_uri=urn%3Aietf%3Aparams%3Aoauth...4ltcg4eY28s
The user's browser sends a GET request to the authorization server using the authorization request URL received by the client application. The authorization server retrieves the authorization request identified by the
request_uri
value, and from now on, the usual steps of the requested OAuth flow are carried out.
As you can see, the browser's request no longer contains the details of the authorization request. There is no risk of tracking or in-transit manipulation of the request. Also, the complexity of the URL is considerably reduced.
PAR Metadata
OAuth and OpenID Connect servers publish the configuration metadata in the /.well-known
URI to publicly declare the supported capability and other protocol details. PAR specification adds two new keys to this metadata:
pushed_authorization_request_endpoint
. This key defines the PAR endpoint where the client application can push the authorization request and get the correspondingrequest_uri
.require_pushed_authorization_requests
. This key has a boolean value indicating whether using PAR is mandatory (true
) or optional (false
). If this key is not present, the default value isfalse
.
With this metadata, your application can check if an authorization server supports PAR using the usual discovery approach.
PAR and Auth0
Auth0 supports PAR as part of the Highly Regulated Identity add-on of the Enterprise plan. Highly Regulated Identity is Auth0's implementation of FAPI 1, the set of standards that raises the security level to protect application operations in sensitive contexts, such as finance, healthcare, e-commerce, etc.
By default, PAR is not active in an Auth0 tenant. You have to enable it at the tenant level to make the PAR endpoint available to your applications. You can do it through your Auth0 dashboard. Follow the steps described here to enable PAR in your Auth0 tenant.
Once you enable PAR at the tenant level, your applications can use the classic authorization endpoint or the PAR endpoint. If you want to make the endpoint mandatory for a specific application, you should require it at the Auth0 application configuration level. You can use the Auth0 dashboard or the Auth0 Management API to set PAR endpoint mandatory. Follow these instructions to enable mandatory PAR for your application.
Conclusion
Pushed Authorization Requests (PAR) are a great addition to strengthen the security of OAuth 2.0 and OpenID Connect protocols. As you have learned, it enables client applications to make authorization requests without exposing the details of the call to potential tracking and manipulation.
This security improvement is critical in scenarios where data confidentiality must be preserved, and Auth0 offers PAR support through Highly Regulated Identity (HRI). Combined with other strong security enhancements supported by HRI, you can build applications with high-security standards and comply with the most common regulations. Check out this document to learn more about Highly Regulated Identity.