JWT Secured Authorization Requests (JAR)

JWT Secured Authorization Requests (JAR)

Introduction

Between the security concerns often raised to OAuth 2 deployments are the exposure of authorization requests parameters and how they can be easily tampered. This was addressed by OpenID Connect protocol by introducing Requests Objects on authorization request, but it was restricted to OpenID Foundation.

With the JWT Secured Authorization Requests (or JAR for short), the IETF brings the option for serializing the authorization request in a JWT format. The specifications are very close but there are some differences on how to process JAR and OIDC Requests Object in the context on the authorization requests. If you are looking for the OIDC Request Object, check Using Request Objects note.

Using a signed JWT structure for the authorization request, the server can validate if the request was tainted and the source of the request can be authenticated. Encryption is also available, allowing the authorization requests to be transported publicly with the proper secrecy required by the use case.

This article describes how AS and clients can leverage JAR support on Authlete.

Authlete support for JAR

The JAR related configuration can be done in two levels on: Service and Client based.

Configuring JAR configuration on service level

The administrator can define the security requirements to JAR and its processing across the clients. You can find the JAR related parameters grouped under “Request Object” section on Authorization tab of the Service Owner console.

The most impactful parameter is the “Request Object Processing”, as its background is the  conflict between OpenID specification and JAR. This parameter changes how the authorization requests parameters are processed when they are duplicated in the request URI and JWT are processed.  For a more detailed description of the differences, check the article Implementer’s note about JAR under section “Conflicts”.

When running with “JAR compatible” selected, the parameters on the request URI are ignored. When running on “Backward compatible” the parameters are merged. If you are configuring Authlete service for FAPI, check the note Configuring Authlete for FAPI compliance.

Parameter  Description
Request Object if Mandatory , every authorization request, including pushed authorizations, is required to be in JWT format. If Optional , the administrator can require JAR for individual clients.
Request Object Processing check description above
nbf Claim if Required, the JWT must contain a “Not Before” claim, this will force the requests to be time boxed. This reduce the attack vectors on AS.
Encryption In Front Channel If Required, the JWT send to /api/auth/authorization endpoint are required to be encrypted. In other words, if the authorization request is not pushed, it will be required to be encrypted to ensure that authorization request is not exposed on user agent.If Optional , the administrator can require JAR for individual clients.
Encryption Algorithm Match when Required the encryption algorithm of key transport (the Content Encryption Key - CEK) used on JWT must match the client level config. If Optional , the administrator can require JAR for individual clients.
Encryption Encoding Algorithm Match when Required the encryption algorithm of the JWT must match the algorithm defined on the client level. If Optional , the administrator can require JAR for individual clients.
Screen_Shot_2021-08-04_at_08
JAR related configuration on Authlete Service Owner console

Configuring JAR support on client level

There are 2 configuration portions per client that are available to administrators on Developers Console.

Setup required keys

For the usage of JAR requests, the client must be configured with public keys using the JWK format. This is available under JWK Set tab for the specific client.

Authlete allows the JWK Set to be assigned directly to the client, using the JWK Set Content attribute on the image below, or a URL pointing to a public JWK endpoint containing the keys used to sign and optionally encrypt the JAR.

Screen_Shot_2021-08-04_at_08
JWK Set configuration per client on Developer console

 JAR requirements on client  level

Authlete allow the administrator to tight the security for specific client by requiring the usage of JAR, encryption of it and the usage of specific algorithms during encryption. The opposite direction is not available: For instance if the service is configure to require JAR, every client will be required, regardless if the client is configured for Optional.

Description
Request Object if Mandatory , every authorization request of this client, including pushed authorizations, is required to be in JWT format. Considered only if the service is configured for Optional .
Request Object Signature Algorithm The algorithm to be used by the client to sign its Request Objects. if not defined, the client can use any algorithm supported by Authlete.The algorithm selected here must have a key for the same algorithm under JWK Set Content or JWK Set URI
Request Object Encryption Algorithm The encryption algorithm for key transport used on JWT. As the Signature, a public key needs to be available under JWK Set Content for the selected algorithm.For instance, if RSA-OAEP is selected, a key with the same alg must be available on JWK Set.
Request Object Encryption Encoding Algorithm The encryption algorithm for the JWT content.
Request URIs List of preregistered request_uri’s that this client can use for authorization. The intent here is to allow authorization requests to be created, signed and encrypted before hand.
Encryption In Front Channel If Required the JWT send to /api/auth/authorization endpoint are required to be encrypted. In other words, if the authorization request is not pushed, it will be required to be encrypted. This will ensure that authorization request is not exposed to user agent.Considered only if the service is configured for Optional .
Encryption Algorithm Match when Required the encryption algorithm of key transport used on JWT must match the client level config. Considered only if the service is configured for Optional .
Encryption Encoding Algorithm Match when Required the encryption algorithm of the JWT must match the algorithm defined on the client level. Considered only if the service is configured for Optional .
Screen_Shot_2021-08-04_at_08
JAR configuration for individual clients available via Developer Console

JWT Authorization Request structure

The structure of the JWT containing the authorization request is a direct map from uri parameters do JWT claims like below:

Authorization Request (URI) JAR equivalent
response_type=code
&client_id=3280859750204
&redirect_uri=https%3A%2F%2Fmobile.example.com%2Fcb
&code_challenge=W78h…uAFaT4
& code_challenge_method=S256
{
“iss” : “3280859750204”,
“response_type” : “code”,
“client_id” : “3280859750204”,
“redirect_uri”  : “https://mobile.example.com/cb",
“code_challenge”:“W78h…uAFaT4”,
“code_challenge_method”: “S256”,
“aud” : “https://as.example.com/"
}

The “iss” and “aud” claims are mapped from the client_id and the issuer configured on Authlete service respectively.

For the signing and encryption, check the libraries available to your env and language.

Signed JAR

Signed JAR is the most basic form, as JAR specification requires that JWT to be signed. The clients have available multiple libraries that support JWT signing as per specification. Check a list of libraries available for your platform.

Regardless of submitting the JAR via URI on authorization request or via body of PAR  authorization, the structure of the JWT and its content is the same.

For instance:

Authorization Request  JAR content
response_type=code
&client_id=3991284690217797
&redirect_uri=https://mobile.example.com/cb
&scope=openid
&state=af0ifjsldkj
&code_challenge=2vChdex6dr…VAB0
&code_challenge_method=S256
{
  “iss”: “3991284690217797”,
  “aud”: “https://authlete.com”,
  “response_type”: “code”,
  “client_id”: “3991284690217797”,
  “redirect_uri”: “https://mobile.example.com/cb",
  “scope”: “openid”,
  “state”: “af0ifjsldkj”,
  “code_challenge”: “2vChdex6dr…VAB0”,
  “code_challenge_method”: “S256”,
  “iat”: 1628124927,
  “nbf”: 1628124927,
  “exp”: 1628125532,
  “jti”: “A_erxcM69yNFZ22jqQXm2”
}

That JWT content when signed using RSA256 and a random key created using mkjwk will be as below

eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6InNpZzEifQ.
eyJpc3MiOiIzOTkxMjg0NjkwMjE3Nzk3IiwiYXVkIjoiaHR0cHM6Ly9hdXRobGV0ZS5jb
20iLCJyZXNwb25zZV90eXBlIjoiY29kZSIsImNsaWVudF9pZCI6IjM5OTEyODQ2OTAyMT
c3OTciLCJyZWRpcmVjdF91cmkiOiJodHRwczovL21vYmlsZS5leGFtcGxlLmNvbS9jYiI
sInNjb3BlIjoib3BlbmlkIiwic3RhdGUiOiJhZjBpZmpzbGRraiIsImNvZGVfY2hhbGxl
bmdlIjoiMnZDaGRleDZkcmRtZnpKSmFDVXBiSHpDVl9Hb3dvTXlQeXJrRUNYVkFCMCIsI
mNvZGVfY2hhbGxlbmdlX21ldGhvZCI6IlMyNTYiLCJpYXQiOjE2MjgxMjQ5MjcsIm5iZi
I6MTYyODEyNDkyNywiZXhwIjoxNjI4MTI1NTMyLCJqdGkiOiJBX2VyeGNNNjl5TkZaMjJ
qcVFYbTIifQ.IRvOu10QHcvi1BgYCS43N7034KxZOMBRL07HupKn4d5cvkah-tnNr8X8b
mgE2SKSAGmK2jp8az7bgKb4g2bBxHd1Crrehxg9tvHswjQCdO7G--MP2M8ey55u5iL-76
S8HU_QiNDXHjuWAmS0godCD2XHxmjUiXjYX2H-cK8vEJy1Pl6-up_ugdJBPYMOtB3AX3w
WYjHGBlabB6S1lUkmksVtJ473zzExW97RVJuU2uFFHdi0XkKFgRRlvqGeZOA8VuKM4te3
qn-XOdV_HCGRPFuPLN1HK5CtM-Rd-iOxjwh3r9xF6cuXPhtxNn_LY7mkK-AEWrqW2jLqR
xZJRxgiXA

The examples below shows the same JWT sent directly to authorization endpoint and pushed to par endpoint. Both examples shows the same request with different security properties.

Authorization request

The sample below shows the request the authorization server can make to Authlete authorization endpoint to submit the JAR request above as request uri, or front-channel. Be aware that any other parameter on the parameter content will be ignored as per JAR specification requirement.

curl --request POST \
   'https://api.authlete.com/api/auth/authorization' \
   --header 'Content-Type: application/json' \
   --header 'Authorization: Basic O....PaEU=' \
--data '{
   "parameters":
     "client_id=3991284690217797&
      request=eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6InNpZzEifQ.
eyJpc3MiOiIzOTkxMjg0NjkwMjE3Nzk3IiwiYXVkIjoiaHR0cHM6Ly9hdXRobGV0ZS5jb
20iLCJyZXNwb25zZV90eXBlIjoiY29kZSIsImNsaWVudF9pZCI6IjM5OTEyODQ2OTAyMT
c3OTciLCJyZWRpcmVjdF91cmkiOiJodHRwczovL21vYmlsZS5leGFtcGxlLmNvbS9jYiI
sInNjb3BlIjoib3BlbmlkIiwic3RhdGUiOiJhZjBpZmpzbGRraiIsImNvZGVfY2hhbGxl
bmdlIjoiMnZDaGRleDZkcmRtZnpKSmFDVXBiSHpDVl9Hb3dvTXlQeXJrRUNYVkFCMCIsI
mNvZGVfY2hhbGxlbmdlX21ldGhvZCI6IlMyNTYiLCJpYXQiOjE2MjgxMjQ5MjcsIm5iZi
I6MTYyODEyNDkyNywiZXhwIjoxNjI4MTI1NTMyLCJqdGkiOiJBX2VyeGNNNjl5TkZaMjJ
qcVFYbTIifQ.IRvOu10QHcvi1BgYCS43N7034KxZOMBRL07HupKn4d5cvkah-tnNr8X8b
mgE2SKSAGmK2jp8az7bgKb4g2bBxHd1Crrehxg9tvHswjQCdO7G--MP2M8ey55u5iL-76
S8HU_QiNDXHjuWAmS0godCD2XHxmjUiXjYX2H-cK8vEJy1Pl6-up_ugdJBPYMOtB3AX3w
WYjHGBlabB6S1lUkmksVtJ473zzExW97RVJuU2uFFHdi0XkKFgRRlvqGeZOA8VuKM4te3
qn-XOdV_HCGRPFuPLN1HK5CtM-Rd-iOxjwh3r9xF6cuXPhtxNn_LY7mkK-AEWrqW2jLqR
xZJRxgiXA"
 }'

Authlete authorization endpoint will be process the JAR request, validate it and having a valid authorization request, the response will resemble the structure below. It seems like a regular authorization response (with the action and ticket attributes), but also includes the requestObjectPayload, which is the JSON content of the JAR request.
if the authorization server is required to reason about the authorization request and further process it before proceeding. the object payload can be parsed and processed accordingly.

{
    "type": "authorizationResponse",
    "resultCode": "A004001",
    "resultMessage": "[A004001] Authlete has successfully issued a ticket to the service (API Key = 943876981845) for the authorization request from the client (ID = 3991284690217797). [response_type=code, openid=true]",
    "action": "INTERACTION
",
    ...
    "requestObjectPayload": "{\"iss\":\"3991284690217797\",\"aud\":\"https://authlete.com\",\"response\_type\":\"code\",\"client\_id\":\"3991284690217797\",\"redirect\_uri\":\"https://mobile.example.com/cb\",\"scope\":\"openid\",\"state\":\"af0ifjsldkj\",\"code\_challenge\":\"0e7Vv0nQLZhCBjJdl7lLa3I4\_Qp3xX00dLRMWnq-rh8\",\"code\_challenge\_method\":\"S256\",\"iat\":1628124632,\"nbf\":1628124632,\"exp\":1628125237,\"jti\":\"KOQrfnS3vsZdIZo-lNYaf\"}",

    ...
    "ticket": "fphPe4GEmYVlrKv6IzCwsPoUQyn9ooE1Yk7ctrTMp-E
"
}

Pushed Authorization Request

The exactly same JAR can be pushed to authorization server and /api/pushed_auth_req counterpart is available for you. The PAR request to Authlete from authorization server will take the form as below

curl --request POST \
  'https://api.authlete.com/api/pushed_auth_req' \
  --header 'Authorization: Basic O....PaEU=' \
  --header 'Content-Type: application/json' \
  --data '{
      "parameters": "client_id=3991284690217797
      &request=eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6InNpZzEifQ.
      eyJpc3MiOiIzOTkxMjg0NjkwMjE3Nzk3IiwiYXVkIjoiaHR0cHM6Ly9hdXRobGV0ZS5jb
      20iLCJyZXNwb25zZV90eXBlIjoiY29kZSIsImNsaWVudF9pZCI6IjM5OTEyODQ2OTAyMT
      c3OTciLCJyZWRpcmVjdF91cmkiOiJodHRwczovL21vYmlsZS5leGFtcGxlLmNvbS9jYiI
      sInNjb3BlIjoib3BlbmlkIiwic3RhdGUiOiJhZjBpZmpzbGRraiIsImNvZGVfY2hhbGxl
      bmdlIjoiMnZDaGRleDZkcmRtZnpKSmFDVXBiSHpDVl9GdG9dp0_sh7N1QJcE0xBWVQ0ZG
      JlZmdGY2xlaG5mRzNWJHJt6YyG0FDI2rDBoDESdiaRN0bnh\_jyaDNrhKLaTBU6TDBv0Q9
      LkN-KNHoXY-ynQxcnW0_jrTq7VQZWfDqV0GRkFBPC093cVpDaU5uQl9jS0RWbuyLCJpYXQ
      iOjE2MjgxMjQ5MjcsIm5iZiI6MTYyODEyNDkyNywiZXhwIjoxNjI4MTI1NTMyLCJqdGkiO
      iJBX2VyeGNNNjl5TkZaMjJqcVFYbTIifQ.IRvOu10QHcvi1BgYCS43N7034KxZOMBRL07Hu
      pKn4d5cvkah-tnNr8X8bmgE2SKSAGmK2jp8az7bgKb4g2bBxHd1Crrehxg9tvHswjQCdO7G
      --MP2M8ey55u5iL-76S8HU_QiNDXHjuWAmS0godCD2XHxmjUiXjYX2H-cK8vEJy1Pl6-up
      _ugdJBPYMOtB3AX3wWYjHGBlabB6S1lUkmksVtJ473zzExW97RVJuU2uFFHdi0XkKFgRRlv
      qGeZOA8VuKM4te3qn-XOdV_HCGRPFuPLN1HK5CtM-Rd-iOxjwh3r9xF6cuXPhtxNn_LY7mk
      K-AEWrqW2jLqRxZJRxgiXA
",
      "clientId": "3991284690217797"
    }'

Authlete will process the JAR, provision a request_uri as a regular PAR (check the note Pushed Authorization Request ) and return the responseContent for the authorization server so that it can be respond to the client accordingly.

{
    "type": "pushedAuthReqResponse",
    "resultCode": "A245001",
    "resultMessage": "[A245001] Successfully registered a request object for client (3991284690217797), URI is urn:ietf:params:oauth:request_uri:Fx4Ue-1EAcaqJyoouv-bFImGNLaZvdMc2DmzT35mUdA.",
    "action": "CREATED
",
    "requestUri": "urn:ietf:params:oauth:request_uri:Fx4Ue-1EAcaqJyoouv-bFImGNLaZvdMc2DmzT35mUdA",
    "responseContent": "{\"expires_in\":600,\"request_uri\":\"urn:ietf:params:oauth:request_uri:Fx4Ue-1EAcaqJyoouv-bFImGNLaZvdMc2DmzT35mUdA\"}
"
}

When the client invokes the authorization using the request_uri, the authorization kicks in and Authlete will process it, creating the ticket and returning the action to be taken by the authorization server, as a regular PAR request .

curl --request POST \
  'https://api.authlete.com/api/auth/authorization' \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Basic O...PAeu=' \
  --data-raw '{
    "parameters": "client_id=3991284690217797&
    request\_uri=urn:ietf:params:oauth:request\_uri:Fx4Ue-1EAcaqJyoouv-bFImGNLaZvdMc2DmzT35mUdA
"
}'

As the authorization request response shown on the previous section, the content of the JWT request, is returned to the authorization server for further inspection and processing.

{
    "type": "authorizationResponse",
    "resultCode": "A004001",
    "resultMessage": "[A004001] Authlete has successfully issued a ticket to the service (API Key = 943876981845) for the authorization request from the client (ID = 3991284690217797). [response_type=code, openid=true]",
    "action": "INTERACTION
",
    ....
    "requestObjectPayload": "{\"iss\":\"3991284690217797\",\"aud\":\"https://authlete.com\",\"response\_type\":\"code\",\"client\_id\":\"3991284690217797\",\"redirect\_uri\":\"https://mobile.example.com/cb\",\"scope\":\"openid\",\"state\":\"af0ifjsldkj\",\"code\_challenge\":\"0e7Vv0nQLZhCBjJdl7lLa3I4\_Qp3xX00dLRMWnq-rh8\",\"code\_challenge\_method\":\"S256\",\"iat\":1628124632,\"nbf\":1628124632,\"exp\":1628125237,\"jti\":\"KOQrfnS3vsZdIZo-lNYaf\"}",

    ...
    "ticket": "ntv32-wAkXNSNP9f-11NCHaTQ9RyBSq\_xbKoyjr0Tvk"

}