JWT Secured Authorization Response Mode (JARM)
On this page
The OAuth and OIDC Request Objects and Pushed Authorization Requests (PAR) define mechanisms for securing the authorization request. JWT Secured Authorization Response Mode (JARM), on the other hand, addresses the authorization response. The specification defines new values for the response_mode
so that the client can request to receive the authorization response in JWT format. The returned JWT includes all the authorization or error response parameters plus some additional claims for further protection.
Specification
JARM defines new response modes for authorization responses. This means that it works with any response type. Via specifying the response mode, the client tells the authorization server how to craft the authorization response. Using JARM, the authorization server can transfer a JWT in the query or fragment part of the redirect URL, or via an auto-submitted form (body).
The new values defined by JARM for the response_mode
are therefore:
query.jwt
fragment.jwt
form_post.jwt
jwt
The response mode jwt
has a special meaning. It is a shortcut for the default value. The default JWT response mode for the code flow (response_type=code
) is defined as query.jwt
whereas for the implicit flow and for the hybrid flow the default is fragment.jwt
. For simplicity, the client can just specify response_mode=jwt
and the authorization server will return the response using the default mode based on the response_type
.
The payload of the JWT depends on the flow, i.e. the value of the response_type
parameter in the request and corresponding authorization response. For example, in a code flow (response_type=code
) the server is supposed to return the code
and state
parameters. In JARM those parameters are part of the JWT payload:
{"exp": 1657190372,"iss": "https//login.example.com/oauth/v2/oauth-anonymous","aud": "web-client","iat": 1657190352,"purpose": "authz_response","code": "M5JfXfDZ0UXHLEdTxEpN7EdPBLzCjyV9","state": "1599045135410-jFe"}
The JWT also contains at least the issuer (iss
), audience (aud
) and expiration time (exp
). The client must decrypt the JWT if encrypted, and validate it. First, the client must verify the issuer as well as the audience against expected values. Then the client validates the expiration time and signature. Only if the JWT passes all checks, the client may make use of the authorization response parameters and proceed.
This article includes some examples for different flows and response modes. For the details of authorization request and corresponding response parameters refer to the documentation of OAuth Flows.
Why JARM?
Implementing the JWT Secured Authorization Response Mode may simply be a question of compliance, i.e. when having to comply with FAPI 1.0 - Advanced Profile. JARM mitigates risks where an attacker can manipulate or read the authorization response. Because the authorization response is returned as a JWT, its parameters are either signed, or signed and encrypted, which means that a man in the browser (or man in the middle) cannot easily change parameters in the response unnoticed or, when applying encryption, cannot even read the data. In other words JARM provides integrity and privacy protection.
JARM also prevents a so-called Mix-Up attack. This attack can occur when a client integrates with several authorization providers, a scenario common in Open Banking where each financial service provider maintains its own authorization server. The attack works, because the client cannot - without any mitigation - distinguish the authorization response from an honest authorization server from one sent by the attacker's authorization server. Consequently, the client easily happens to mix the request to one authorization server with the response from another because it cannot verify that the response comes from the authorization server that it started the flow at. When encoding the authorization response in a JWT, the JWT contains an issuer claim, thus the client can verify that the response it receives comes from the correct authorization server.
Examples
Code Flow and query.jwt
The client starts the code flow with an authorization request sending response_type=code
and response_mode=jwt
together with the other parameters of the authorization request. In the following example, the client makes also use of the state
parameter and PKCE (line breaks for display purposes):
GET /oauth/v2/oauth-authorize? HTTP/1.1Host: login.example.comContent-Type: application/x-www-form-urlencodedclient_id=web-client&response_type=code&redirect_uri=https://www.example.com/callback/code&state=1599045135410-jFe&scope=read&code_challenge=rerbvXfTDYNECzwayM8-SLCWU1FDzBnqMCv1RB5AudU&code_challenge_method=S256&response_mode=jwt
The default JWT response mode for the code flow is query.jwt
. Thus, the authorization server returns the response as a JWT in the query parameter of the HTTP message (line breaks for display purposes):
HTTP/2 302 FoundLocation: https://www.example.com/callback/code?response=eyJraWQiOiI4ODEyMDM5NTUiLCJ4NXQiOiJWNE14UU5ZX0o1Tjl5eXdHOEkyajJhQ2JnaFEiLCJhbGciOiJSUzI1NiJ9.eyJleHAiOjE2NTcxOTAzNzIsImlzcyI6Imh0dHBzLy9sb2dpbi5leGFtcGxlLmNvbS9vYXV0aC92Mi9vYXV0aC1hbm9ueW1vdXMiLCJhdWQiOiJ3ZWItY2xpZW50IiwiaWF0IjoxNjU3MTkwMzUyLCJwdXJwb3NlIjoiYXV0aHpfcmVzcG9uc2UiLCJjb2RlIjoiTTVKZlhmRFowVVhITEVkVHhFcE43RWRQQkx6Q2p5VjkiLCJzdGF0ZSI6IjE1OTkwNDUxMzU0MTAtakZlIn0.QoFtbRF69W8-rXvjz3UwSb3LjK38ioN5qkFyY1qZE7y9P08Oh39c2x3evOl19Q5I8IrfRI94xdRN4kdTmg046ggcczDb4VkPKSeB2YcCfZzUHulS6XSKf-eJsszhsyeOIp8y-wpu7lnm4KZXfq3BmIZcl49q2Ac84VXyXX6-LOQ04Twm4nZYe6yenzUNE4J5hTcvzrgjShQPs8NqyHsaimNHcqilolj8PvQLJC-TNSasZGI0OjmUxa8msJD-kq2fNJFoVpEIrmZthXHLUUu3N67MBEEih-tskX8DwDpPxvW8iSi3PkkOc1acH_hsXSll0dvJ_p38-Dyz7rz883C6mA
The response parameter represents the JWT with the code
and state
from the authorization response as well as the issuer, audience and expiration time along with other claims in the payload.
{"exp": 1657190372,"iss": "https//login.example.com/oauth/v2/oauth-anonymous","aud": "web-client","iat": 1657190352,"purpose": "authz_response","code": "M5JfXfDZ0UXHLEdTxEpN7EdPBLzCjyV9","state": "1599045135410-jFe"}
The client must validate the JWT before it checks the state and exchanges the code for a token.
Implicit Flow and fragment.jwt
The client starts the implicit flow with an authorization request sending response_type=token
and response_mode=jwt
together with the other parameters of the authorization request.
GET /oauth/v2/oauth-authorize HTTP/1.1Host: login.example.comContent-Type: application/x-www-form-urlencodedclient_id=web-client&redirect_uri=https://www.example.com/callback/implicit&response_type=token&state=1599045172021-mww&scope=read&response_mode=jwt
The default JWT response mode for the implicit flow is fragment.jwt
. Thus, the server returns the response as a JWT in a URL fragment (line breaks for display purposes).
HTTP/2 302 FoundLocation: https://www.example.com/callback/implicit#response=eyJraWQiOiI4ODEyMDM5NTUiLCJ4NXQiOiJWNE14UU5ZX0o1Tjl5eXdHOEkyajJhQ2JnaFEiLCJhbGciOiJSUzI1NiJ9.eyJleHAiOjE2NTcyMTg4MDgsImlzcyI6Imh0dHBzLy9sb2dpbi5leGFtcGxlLmNvbS9vYXV0aC92Mi9vYXV0aC1hbm9ueW1vdXMiLCJhdWQiOiJ3ZWItY2xpZW50IiwiaWF0IjoxNjU3MjE4Nzg4LCJwdXJwb3NlIjoiYXV0aHpfcmVzcG9uc2UiLCJhY2Nlc3NfdG9rZW4iOiJfMFhCUFdRUV9lYzM4Mzc5OC1iMTM2LTRkZTEtYjE5MC01OTBhZjBkZDFmZmYiLCJzdGF0ZSI6IjE1OTkwNDEyMzQ4NTkiLCJ0b2tlbl90eXBlIjoiYmVhcmVyIiwiZXhwaXJlc19pbiI6IjMwMCIsInNjb3BlIjoicmVhZCJ9.HM8efWTQDenRN-Q8xpfVaHfhZo6wE86HTX6EnktnUBRb4c9qHPs2HgbAZwThTRb-Men3WXrd929Obqv6JY9_n1Vs65U5lMcb5G2W1KjIaPquBG7MxBG2q_B3hq6uhzk5mOlqPxIQwQ8VIMiaIGY892_TA8cZlFIZzbvPViZj9VBy2JaZHlEhLdsTXSF7_RThMNdput4pz1D2BTuQgk0MBJvAyJWU9H9wMNlQq89m1_ztfluLXfMj5bvYqpCqEueOvWHlyENVp0SUdOUpNGsFqOwphXPH1z5EVR7_0On2d3QEMWkunytOEdl8bpXi-hE9y-hVCBBvPXi9_fv8qUji7A
A URL fragment and consequently the JWT response is only accessible from the browser, e.g. JavaScript.
The JWT contains the access_token
, token_type
, expires_in
, scope
and state
claims that are the authorization response parameters of the implicit flow.
{"exp": 1657218808,"iss": "https//login.example.com/oauth/v2/oauth-anonymous","aud": "web-client","iat": 1657218788,"purpose": "authz_response","access_token": "_0XBPWQQ_ec383798-b136-4de1-b190-590af0dd1fff","state": "1599041234859","token_type": "bearer","expires_in": "300","scope": "read"}
The client must validate the JWT before it is allowed to use the access token.
Example - Hybrid Flow and post_form.jwt
It is possible to send the response in the body of a request using response_mode=post_form.jwt
.
Study the following example of a hybrid flow that uses response_type=code id_token
and response_mode=post_form.jwt
.
The example makes use of the state
parameter and PKCE (line breaks for display purposes).
GET /oauth/v2/oauth-authorize HTTP/1.1Host: login.example.comContent-Type: application/x-www-form-urlencodedclient_id=web-client&response_type=code%20id_token&redirect_uri=https://www.example.com/callback/hybrid&state=1599045202487-sVG&scope=openid%20%20read&code_challenge=y2upJc5ieYeoQaU0c25iG87qxEXXC7h_0LvIYgQ6ufk&code_challenge_method=S256&nonce=1657220314981-cBR&response_mode=post_form.jwt
The response is an HTTP form with the JWT that the browser automatically submits to the redirect_uri
(line breaks and abbreviation for display purposes):
HTTP/2 200 OKContent-Type: text/html;charset=UTF-8Cache-Control: no-cache, no-storePragma: no-cache<html><head><title>Submit Authorization Response with Form</title></head><body onload="javascript:document.forms[0].submit()"><form method="post" action="https://www.example.com/callback/hybrid"><input type="hidden" name="response"value="eyJraWQiOiI4ODEyMDM5NTUiLCJ4NXQiOiJWNE14UU5ZX0o1Tjl5eXdHOEkyajJhQ2JnaFEiLCJhbGciOiJSUzI1NiJ9.eyJleHAiOjE2NTcyNjMyNDMsImlzcyI6Imh0dHBzLy9sb2dpbi5leGFtcGxlLmNvbS9vYXV0aC92Mi9vYXV0aC1hbm9ueW1vdX...Z5TUtiR2h2VCtaSm9VQ2M1L2FETkM0NmZhSXJmWVx1MDAzZC5WMnFObjRsRmRZNTgifQ.uJ86oH6IHLTJkfkPygNT35YscyFpQ5Nz_MS-l92oYoL15jTXBaus1jPgFExYzpb104Fsu4-Z1WZrQkGGh8xjDmupVzXEEJUJAFh4AI4jVWriptXwHEdN8icMYdEUrbv8cJWe5I-A5MdH5oNr1KX2fHPrrDMqCF1br8Gg48w9ze_kw5n7BakPUzhBrRkMTcH19yIxbDTCXlZEMhxEbPaY3ESxC0_xyNMImSVBeUX_TJy_IPYH1ozodVz8jDYqrplikCOEf1joX1eRDrZZdEDTdaHus39I0PK7Wn_WCDcC-v2e6cC_zSvq6pUZgTQ1dAGBHs4vHdjHCtfQafEkmezYQA"/></form></body></html>
The client requested an ID token together with the code thus the JWT contains claims for both (abbreviation for display purposes):
{"exp": 1657263243,"iss": "https//login.example.com/oauth/v2/oauth-anonymous","aud": "web-client","iat": 1657263223,"purpose": "authz_response","id_token": "eyJraWQiOiI4ODEyMDM5NTUiLCJ4NXQiO...acz3ofJIia2IFwR5Re6sdOHuiA","code": "xD04zGiv7S5caUN9iaWLEeeLR3EeDRyw","state": "1599045202487-sVG","session_state": "tRvKMTUQFyNNPFyMKbGhvT+ZJoUCc5/aDNC46faIrfY=.V2qNn4lFdY58"}
The client must validate the JWT before it uses any of the claims for further processing.
Conclusion
JWT Secured Authorization Response Mode is enforced by the client. It allows the client to request an authorization response in JWT form. JWTs provide integrity protection via signatures and privacy protection via encryption - requirements common in financial-grade APIs.
JWTs are a very popular format for access tokens and ID tokens are always encoded as JWTs, thus clients are familiar with the format and commonly already have JWT processing capabilities. Returning the authorization response in JWT form is an easy and simple mechanism to protect the messages from the server.
Judith Kahrer
Product Marketing Engineer at Curity
Join our Newsletter
Get the latest on identity management, API Security and authentication straight to your inbox.
Start Free Trial
Try the Curity Identity Server for Free. Get up and running in 10 minutes.
Start Free Trial