Implementer’s note about JAR (JWT Secured Authorization Request)
By: Takahiko Kawasaki
Tags: Fapi, Par, Oidc, Jar, Oauth

Introduction

This article is an implementer’s note about a technical specification called “JAR”, The OAuth 2.0 Authorization Framework: JWT Secured Authorization Request.

There exist some conflicts between OpenID Connect Core 1.0 (OIDC Core) and JAR. The OAuth community had a long dispute about the breaking changes because they affect existing authorization server implementations and even the OpenID conformance suite.

What are the conflicts? How were they solved? This article describes them for future readers and implementers of the JAR specification.

What is JAR?

JAR is a mechanism whereby to pack authorization request parameters into a signed JWT. A client application sends the JWT to the server (via a user agent) instead of listing the request parameters separately.

The JWT is called “request object”. It is sent to the server as the value of the request parameter, or its location is sent as the value of the request_uri parameter.

For example, the following authorization request which has 7 separate query parameters:

becomes like below with the request parameter,

or like below with the request_uri parameter.

All the 7 request parameters are put in the request object, and at the same time, the client_id parameter is present outside the request object duplicately.

An early draft of JAR was so bold to drop client_id and require that either request or request_uri be used as the only parameter of an authorization request. However, most of active authorization server implementers insisted that client_id should remain mandatory. Why? The reasons are as follows.

If a request object is encrypted with a symmetric algorithm, the shared secret key is needed to decrypt it. Because the key for symmetric algorithms is the client secret (OIDC Core Section 10.1), the authorization server must be able to identify the client application to extract the client secret from its database before decrypting the request object. Even if the encrypted request object includes client_id inside it, the authorization server cannot know the value before decryption. If a request object is specified by request_uri , the authorization server must fetch the request object from the location. However, for security reasons, the authorization server may (I think it “should”) refuse to fetch a request object from an unregistered location (cf. OpenID Connect Discovery 1.0, Section 3, require_request_uri_registration ). To collate the presented request URI with pre-registered ones of the client application, the authorization server must be able to identify the client application before fetching the request object. Even if the remote request object includes client_id inside it, the authorization server cannot know the value before fetching.

Conflicts

Request object was defined as a part of OIDC Core (Section 6) in 2014. JAR’s purpose is to extract the specification of request object from OIDC Core and make it usable for generic purposes. Those involved thought the task wouldn’t take much time, but over 6 years have passed since the first draft before breaking changes introduced by JAR got consent from the OAuth community.

Request Parameter Assembly

In OIDC Core, request parameters inside a request object and ones outside the request object are merged as Section 6.3.3 explictly states as follows.

The Authorization Server MUST assemble the set of Authorization Request parameters to be used from the Request Object value and the OAuth 2.0 Authorization Request parameters (minus the request or request_uri parameters). If the same parameter exists both in the Request Object and the OAuth Authorization Request parameters, the parameter in the Request Object is used.

On the other hand, JAR prohibits referring to request parameters outside a request object by stating as follows in Section 5,

However, the authorization server supporting this specification MUST only use the parameters included in the request object.

and in Section 6.3.

The Authorization Server MUST only use the parameters in the Request Object even if the same parameter is provided in the query parameter.

response_type

In OAuth 2.0 (RFC 6749) and OIDC Core, response_type is a mandatory parameter. Even if a request object includes response_type , OIDC Core requires response_type outside the request object. Section 6.1 of OIDC Core explicitly states as follows.

So that the request is a valid OAuth 2.0 Authorization Request, values for the response_type and client_id parameters MUST be included using the OAuth 2.0 request syntax, since they are REQUIRED by OAuth 2.0. The values for these parameters MUST match those in the Request Object, if present.

Apparently, OIDC Core has taken compatibility with OAuth 2.0 seriously. However, JAR has decided not to require response_type outside a request object in spite of some objections from the community.

As a result, when JAR rules apply, authorization servers that conform to OIDC Core and reject OIDC requests that don’t come with response_type outside a request object are regarded as wrong implementations.

Of course, some people suggested that the OIDC Core’s requirement on response_type should remain effective even when JAR rules apply. However, the reached consensus is “servers must not require the duplicates.” (FAPI Issue 315)

Changing “mandatory” to “optional|ignored” has a bigger impact on implementations than the opposite direction because developers must review and modify source programs that are written on the assumption that mandatory parameters are always available once sanity parameter checks in an early phase are done.

scope

The scope parameter is optional in OAuth 2.0, but OIDC Core has made it mandatory. The following is the description about the scope parameter excerpted from Section 3.1.2.1 of OIDC Core.

REQUIRED. OpenID Connect requests MUST contain the openid scope value. If the openid scope value is not present, the behavior is entirely unspecified. Other scope values MAY be present. Scope values used that are not understood by an implementation SHOULD be ignored. See Sections 5.4 and 11 for additional scope values defined by this specification.

In addition, Section 6.1 requires that scope be present outside a request object duplicately when the request object includes scope and the scope includes openid .

Even if a scope parameter is present in the Request Object value, a scope parameter MUST always be passed using the OAuth 2.0 request syntax containing the openid scope value to indicate to the underlying OAuth 2.0 logic that this is an OpenID Connect request.

If the same conclusion as response_type applies, servers must not require that scope be present duplicately. The relevant discussion could be found in the archive of the OAuth mailing list ([OAUTH-WG] [JAR] scope parameter outside request object of OIDC request).

As a result, when JAR rules apply, authorization servers that conform to OIDC Core and reject OIDC requests that don’t come with scope including openid outside a request object are regarded as wrong implementations.

Signing

JAR requires that a request object be always signed. On the other hand, OIDC Core allows unsigned request objects. Section 6.1 of OIDC Core states as folows.

The Request Object MAY be signed or unsigned (plaintext)

An interesting side effect is that none should be removed from request_object_signing_alg_values_supported in the discovery document (OpenID Connect Discovery 1.0) if the authorization server always forces JAR rules.

FAPI

“FAPI”, Financial-grade API, is a specification for enhanced API security. Because FAPI is built on top of OIDC, FAPI implementations are affected by JAR.

FYI: FAPI Part 2 has some requirements related to request objects as follows.

[FAPI Part 2, Section 5.2.2, Clause 1] shall require the request or request_uri parameter to be passed as a JWS signed JWT as in clause 6 of OIDC;

shall require the or parameter to be passed as a JWS signed JWT as in clause 6 of OIDC; [FAPI Part 2, Section 5.2.2, Clause 10] shall require that all parameters are present inside the signed request object passed in the request or request_uri parameter;

shall require that all parameters are present inside the signed request object passed in the or parameter; [FAPI Part 2, Section 5.2.2, Clause13] shall require the request object to contain an exp claim;

shall require the request object to contain an claim; [FAPI Part 2, Section 8.6, Clause 1] shall use PS256 or ES256 algorithms;

PAR

“PAR”, OAuth 2.0 Pushed Authorization Requests, is another new specification. It enables client applications to pre-register authorization requests into the authorization server and get request URIs that correspond to the registered authorization requests. The request URIs can be used as the value of the request_uri parameter when the client applications make an authorization request later.

The diagram below illustrates the flow of PAR. See “Illustrated PAR: OAuth 2.0 Pushed Authorization Requests” for details.

The reason I mentioned PAR here is that PAR requires that a request object given to the pushed authorization request endpoint be processed based on JAR rules. The following is an excerpt from the PAR specification.

Clients MAY use the request parameter as defined in JAR to push a request object JWT to the authorization server. The rules for processing, signing, and encryption of the request object as defined in JAR apply.

The PAR endpoint issues a request URI that represents the registered authorization request. The value and lifetime of the request URI are contained in the response from the PAR endpoint as the values of request_uri and expires_in like below.

{

"request_uri": "urn:example:bwc4JK-ESC0w8acc191e-Y1LTC2",

"expires_in": 90

}

A confusing point here is that the lifetime of the issued request URI and the expiration time of the registered authorization request are different. The table below shows what correspond to the lifetime and the expiration time.

An easily-overlooked point is that a PAR endpoint implementation must not drop information about the exp claim of a given request object when it saves the authorization request because the exp claim may be referenced later when the issued request URI is used. For example, the validation step for [FAPI Part 2, Section 5.2.2, Clause 13] checks the existence of the exp claim.

Metadata

JAR defines require_signed_request_object metadata for both server and client, respectively.

The description of the boolean metadata in Section 9.2 “OAuth Authorization Server Metadata Registry” is simple as excerpted below.

Indicates where authorization request needs to be protected as Request Object and provided through either request or request_uri parameter

The name and description of the metadata give an impression that the metadata just controls whether a signed request object is required or not. However, you will notice it means much more when you read a paragraph in Section 10.5 “Downgrade Attack”.

When the value of it as a server metadata is true , then the server MUST reject the authorization request from any client that does not conform to this specification. It MUST also reject the request if the request object uses "alg":"none" . If omitted, the default value is false .

That is, "require_signed_request_object":true means (1) that an authorization request must use either request or request_uri AND (2) that the request object is processed and validated based on JAR rules.

The behavior expected in the case of "require_signed_request_object":true is unfortunately inflexible for authorization server implementations that want to support various use cases. Therefore, vendors will provide custom options. For example, Authlete (version 2.2) provides two separate configuration items that correspond to (1) and (2) above. Some other vendors and individuals such as Connect2id and node-oidc-provider also explained their approaches for JAR in the OAuth mailing list in the past. Please dig the mail archive if you are interested.

Example (Authlete)

Authlete’s configuration items for Request Object

[Request Object]

If Mandatory is selected, authorization requests to this service must always specify a request object by using request or request_uri parameter.

If Mandatory is selected here and JAR compatible is selected in Request Object Processing, this service behaves as if require_signed_request_object server metadata is true . See the specification of JAR (JWT Secured Authorization Request) for details about the server metadata.

[Request Object Processing]

If JAR compatible is selected, request objects are processed based on the rules defined in JAR (JWT Secured Authorization Request). If Backward compatible is selected, the rules defined in OpenID Connect Core 1.0 apply.

Differences between JAR rules and OIDC Core 1.0 rules are as follows.

JAR requires that request objects be always signed. JAR prohibits referring to request parameters outside the request object. As a result, when JAR rules apply, parameters outside the request object and parameters inside the request object are not merged. OIDC Core 1.0 requires that response_type query/form parameter be present outside the request object even when the request object contains response_type . On the contrary, when JAR rules apply, response_type query/form parameter outside the request object is no longer mandatory. However, instead, it means JAR requires that response_type be always included in the request object. OIDC Core 1.0 requires that an OIDC request come with scope query/form parameter including openid even when the request object contains scope . On the contrary, when JAR rules apply, scope query/parameter parameter outside the request object is no longer mandatory. Note that the request object must contain scope including openid so that the authorization request can be regarded as an OIDC request.

If JAR compatible is selected here and Mandatory is selected in Request Object, this service behaves as if require_signed_request_object server metadata is true . See the specification of JAR for details about the server metadata.

Finally

FAPI version 1 will be finalized by the end of 2020 and FAPI version 2 will start. JAR is to be adopted as a component of FAPI version 2 (cf. “Global Adoption of FAPI Among Open Banking Standards… And Beyond”, Nat’s keynote at FDX Global Summit Fall 2020).

JAR contains some breaking changes, but its adoption is an established policy. Let’s be prepared for the new specification.