Better OAuth 2.0 Authorization with Pushed Authorization Requests
— OAUTH, oidc, pushed authorization requests, par — 2 min read
OAuth is imperfect. So it's great that the specification can be extended to continually improve and adapt with modern authentication and authorization.
One uncomfortable issue is that OAuth and many OpenID Connect implementations cannot validate the authenticity of an authorization request. Meaning that anybody can tweak the parameters of an authorization request mid-flight, before it hits the authorization server. They may add some extra scope items, change the Client ID or replace the redirect URI which could have some unexpected or dangerous outcomes.
Furthermore, personal information may be sent along with the authorization request to provide additional context, this usually travels via your browser, and whilst encrypted over the wire in transit, has to traverse through many software layers between the client and the authorization server, exposing it to various different attacks.
PAR - or "Pushed Authorization Requests" aims to minimize risk in this space by using a back-channel to send authorization requests directly to the authorization server. This significantly reduces the attack surface on the OAuth transaction.
An authorization server that supports PAR should have a PAR endpoint published to its metadata (also known as discovery endpoint or well-known endpoint). This URL accepts HTTPS POST payloads.
So, instead of forming an OAuth Authorization Request using a browser redirect and Query String Parameters, e.g:
&client_id=CLIENT1234&state=idcit1234 &redirect_uri=https%3A%2F%2Fclient.identitycitizen.com%2Fcb HTTP/1.1 Host: as.example.com
(An example flow is illustrated below:)
With PAR - we now make a direct API call to the Authorization Server, instead of sending the authorization parameters via query string, they're sent as a POST body, out of band of regular browser traffic. The client is also authenticated through whichever client authentication mechanism is supported (e.g. client secret orclient assertion).
Host: as.example.com Authorization: Basic ************* Content-Type: application/x-www-form-urlencoded
&response_type=code &client_id=CLIENT1234&state=idcit1234 &redirect_uri=https%3A%2F%2Fclient.identitycitizen.com%2Fcb
The Authorization Server will acknowledge this authorization request and return a reference URI along with a HTTP 201 Created, similar to:
Cache-Control: no-cache, no-store Content-Type: application/json
{ "request_uri": "urn:example:1234-ABCD123_98765", "expires_in": 90 }
This request_uri is provided to the client to call the Authorization Server (e.g via a browser redirect):
&request_uri=urn%3Aexample%3A1234-ABCD123_98765 HTTP/1.1 Host: as.example.com
From here the OAuth flow effectively continues as per normal. In most cases this means the Authorization Server can challenge for authentication and provide an authorization code back to the client to exchange for an access token.
Pushed Authorization Requests hide data that doesn't need to be exposed. It's a pragmatic approach to improving identity security outcomes. PAR is much like PKCE in that several years ago it wasn't widely adopted, but is now considered best practice (to the point that PKCE is required in OAuth 2.1). In fact, OAuth 2.1 has already started referencing PAR for registration scenarios.
So if you're not currently using PAR, start playing with it, spread the word, and do your bit to help strengthen authorization and authentication.