Skip to content

Secure OAuth2 (Part -2): Put it in a JAR (JWT-Secured Authorization Request)

9 min read

In my last post — which I published almost a year ago — I explored whether the authorization code grant type truly provides the highest level of security, and what potential risks it might carry. This grant type is widely regarded as the recommended option when involving end users, both in the OAuth 2.0 best current practices and the OAuth 2.1 draft specification.

If you haven’t already, I highly recommend reading that first post before diving into this one.

Secure OAuth2 : Is Authorization Code Grant Type Secure Enough?
_The authorization code grant type is generally considered as the most secure, widely used and commonly recommended…_sagarag.medium.com

To quickly recap — in the standard Authorization Code grant flow, the authorization request is sent by encoding all the necessary parameters directly into the URL as query parameters this approach is known as query parameter serialization. This request then travels through the user agent (usually a browser) to the authorization server.

While this approach works, it also comes with a few security shortcomings, here are some issues related to authorization request:

P1Integrity protection failure

P2 — Source Authentication Failure

P3 — Confidentiality failure

P4 — Lack of Request as a Reference semantic

The implications tied to the authorization response of the authorization code flow includes:

P5Server integrity protection failure

P6 — Client confidentiality failure

P7 — Mixed-up attack

To start with a bit of history — the questions that I mentioned earlier weren’t really addressed in the original OAuth2 core specification released back in 2012. But if we go back to the basics and think about how we can sign and encrypt the request parameters themselves, we realize that most of these security concerns could be addressed right at the foundation level. Thankfully, we didn’t have to wait too long for a solution — around the same time, a JSON-based token format called JWT started gaining traction, bringing along powerful signing capabilities through JWS and encryption through JWE.

In 2014, the OpenID Connect (OIDC) core specification introduced a concept called the Request Object solving most of the above problem. This is essentially a JWT that encapsulates the request parameters, allowing the request to be signed using JWS and optionally encrypted using JWE. It helped to solve some of the earlier security concerns — but only within the context of OIDC.

Then in 2017, the IETF began working on a new specification inspired by this idea, called JWT-Secured Authorization Request (or JAR). After six years of discussion and iteration, in 2021 JAR finally became an official standard in the OAuth2 ecosystem. Because of that origin, the Request Object concept in OIDC is quite similar to what’s defined in the JAR spec — but it’s worth noting that there are still a few subtle incompatibilities between the two.

Request Object

The Request Object is really the heart of the JAR specification. It allows us to encode each part of the authorization request as individual JWT claims. To get a better sense of how this works, let’s start with a standard authorization request — like the one shown in the figure below.

Next, we translate those query parameter values into a JSON-encoded JWT. The following figure will show you what that same request looks like once it’s been encoded as a JWT.

There are a few rules that you need to follow when doing this. For instance:

  • **iss**(issuer claim) — This one is mandatory. In most cases, it should be the client_id of your application. However, some authorization servers may support a custom value here, depending on prior agreements.
  • **aud** (audience claim) — This should match the issuer value of the Authorization Server (AS) you’re talking to.

The next step is signing the Request Object using JWS. This process involves generating a JOSE header, calculating the signature, and then producing the final JWT in compact format by applying base64url encoding — you can see how final result looks like in the figure below.

One important thing to keep in mind: the public key that matches the private key used for signing needs to be already configured on the authorization server. Ideally, this happens during the application or client registration phase. You can do this using Dynamic Client Registration (DCR) or whatever interface your authorization server provides for managing signature verification keys. This step is critical — without the corresponding public key, the authorization server won’t be able to verify the signature of the Request Object. But once it’s in place, the server can ensure that the Request Object really came from your client and that its content hasn’t been tampered with along the way. This directly addresses two of the earlier issues I mentioned: P1Integrity protection failure and P2 — Source Authentication Failure.

In addition to signing, you can optionally encrypt the signed JWT using JWE, based on a public key that has been agreed upon. This way, the encrypted Request Object can only be decrypted by the authorization server using its private key — which is never shared. This extra step ensures confidentiality between the client and the authorization server, effectively addressing P3 — Confidentiality failure.

So, at this point, by using a signed (and optionally encrypted) Request Object, we’ve tackled first three problems: P1Integrity protection failure , P2 — Source Authentication Failure and P3 — Confidentiality failure. Now, let’s dive into the different ways the JAR specification allows us to send this JWT-secured Request Object to the authorization server.

1. Passing Authorization Request by Value

This is the most straightforward approach: the application takes the signed (or signed and encrypted) Request Object we created earlier and encodes it as the value of the request query parameter. Then, it builds the authorization URL by including this encoded Request Object along with the client_id, and sends it to the authorization server’s authorization endpoint. The diagram below illustrates how this flow works in practice. Other than the way the parameters are sent (now wrapped inside the request parameter as a JWT), there’s really no change to the overall flow when compared to the standard Authorization Code authorization request phase.

And just to make it more clear, I’ve also included a sample authorization request for reference below.

2. Passing Authorization Request by Reference

Instead of directly passing a signed (and optionally encrypted) Request Object in the authorization request, there’s another option — you can store the Request Object in a specific location that the Authorization Server can access. Then, rather than sending the full Request Object through the user agent, you just send a URI pointing to it. The Authorization Server can then fetch the Request Object using that URI to process the authorization request.

This approach is considered more secure because the actual Request Object doesn’t pass through the user agent — it’s only available to the Authorization Server. It’s also a preferred method when dealing with large Request Objects or when your app needs to work on devices with limited processing power or slow internet connections (like mobile devices). The following diagram outlines how this flow works.

During the step-1, your client application creates the Request Object just like we discussed earlier and stores it in a location that’s accessible to the Authorization Server. This storage location can be:

  • A local endpoint hosted by your client application
  • A remote endpoint hosted by the Authorization Server
  • A trusted third-party endpoint

Then comes step 2. In this step, the client application builds the actual authorization request — but instead of including the full Request Object, it uses a new parameter introduced in the JAR specification called request_uri along with the client_id . The request_uri parameter holds the URI pointing to the previously stored Request Object.

The client sends this authorization request to the Authorization Server through the user agent, and the server fetches the actual Request Object using the provided URI. The following example shows what this kind of request looks like.

Once the Authorization Server receives the authorization request from the client application, it recognizes that it needs to fetch the actual Request Object. Using the value provided in the request_uri parameter, it reaches out to that location and retrieves the Request Object to continue processing the request as usual. This approach works really well when you’re dealing with large Request Objects and also offers maximum security. Since the actual Request Object isn’t sent through the user agent, it avoids unnecessary exposure. This neatly solves the earlier mentioned P4 — Lack of Request as a Reference semantic problem.

Validating JWT-Based Requests

Once the Authorization Server receives the Request Object — either directly as an encoded parameter in the authorization request or as a reference (where the server fetches it) — the next step is validation. If the Request Object is encrypted, the Authorization Server first decrypts it as specified by the JWE standard. Since only the Authorization Server has the private key needed for decryption, we can be confident that confidentiality is maintained between the client application and the Authorization Server.

Next, the server moves on to signature validation. It verifies the signature using the public key that was configured during the client application’s registration. A successful signature check ensures that the Request Object hasn’t been tampered with and also confirms that it truly originated from the signed client application.

Just to recap, in this post we explored how JWT-Secured Authorization Request (JAR) enhances the security of the standard authorization code grant type by introducing integrity protection, source authentication, confidentiality, and support for request-by-reference semantics.

It’s been nearly five years since JAR became a standard, and we’re now seeing its concepts — especially the use of Request Objects along with signing and encryption — being adopted across different industry verticals. That said, the idea of passing the Request Object as a reference didn’t quite take off as expected. It turned out to be a bit too vague and open-ended for practical adoption.

This is where the Push Authorization Request (PAR) specification steps in which I’ll dive into in my next post. PAR offers a more robust and standardized way to implement pass-by-reference while still building on the core benefits of JAR. Stay tuned!

Read the other posts in Secure OAuth2 series.

Secure OAuth2 : Is Authorization Code Grant Type Secure Enough?
_The authorization code grant type is generally considered as the most secure, widely used and commonly recommended…_sagarag.medium.com