Securing Authorization Requests and Responses with JAR and JARM
Securing APIs goes beyond implementing strong authentication. It's about protecting the entire OAuth 2.0 flow—from the initial authorization request to the authorization server’s response. Two essential extensions designed to address vulnerabilities in OAuth are:
-
JWT Secured Authorization Request (JAR)
-
JWT Secured Authorization Response Mode (JARM)
These protocols improve the integrity, authenticity, and confidentiality of OAuth messages, reducing the risk of tampering, eavesdropping, and mix-up attacks.
What Is JWT Secured Authorization Request (JAR)?
In a standard OAuth 2.0 authorization request, parameters such as client_id, redirect_uri, scope, and state are transmitted as plain query strings via the user’s browser to the authorization server. This method works but leaves the request exposed to tampering, manipulation, or leakage, especially in environments with browser extensions, proxies, or malicious intermediaries.
JWT Secured Authorization Request (JAR) improves this by wrapping the entire authorization request inside a digitally signed JWT, called a request object. Instead of sending parameters openly, the client creates a signed JWT containing all required values, ensuring the request’s integrity and authenticity.
With JAR, the authorization request becomes a secure, verifiable message—no longer just a set of exposed URL parameters.
For an additional layer of protection, the JWT can also be encrypted, making the request contents confidential—readable only by the authorization server. This is especially useful when requests carry sensitive information, or when full end-to-end confidentiality is required.
How JAR Works
-
Key Setup:
-
The client generates a public/private key pair.
-
The public key is registered with the authorization server.
-
-
Create Request Object:
-
Authorization parameters like
client_id
,redirect_uri
,scope
, andstate
are placed into a JWT. -
The JWT is signed using the client’s private key.
-
-
Send the Request:
-
The signed JWT is sent either:
-
Inline as a
request
parameter in the URL, or -
By reference using a
request_uri
pointing to the hosted JWT.
infoIt is possible to use JAR when sending a request to the OAuth Pushed Authorization Request (PAR) endpoint.
-
-
-
Verification by Server:
-
The authorization server verifies the JWT signature.
-
If valid, it extracts the parameters and proceeds with the flow.
-
Example: Creating Signed Request Object for JAR
The example below demonstrates how to create a signed JWT that includes all necessary OAuth 2.0 parameters, including support for Proof Key for Code Exchange (PKCE), a mechanism that adds an additional layer of security—particularly for public clients like single-page apps or mobile apps that can't securely store a client secret.
This script includes:
-
nonce
to prevent replay attacks. -
PKCE
code_challenge
, derived from a securely generated randomcode_verifier
. -
Signing with an RSA private key, using the RS256 algorithm.
-
type
header of oauth-authz-req+jwt to indicate a JAR payload.
Once generated, the JWT can be passed to the authorization server via the request parameter of the authorization URL.
Sample Code
const jwt = require('jsonwebtoken');
const crypto = require("crypto");
const fs = require('fs');
// Load your private RSA key for signing
const privateKey = fs.readFileSync('[PATH TO YOUR PRIVATE KEY]');
const client_id = '[YOUR CLIENT ID]';
// Generate a random nonce for security
const nonce = crypto.randomBytes(16).toString('hex');
// PKCE: Generate code_verifier and code_challenge
const code_verifier = crypto.randomBytes(32).toString('base64')
.replace(/=/g, '')
.replace(/\+/g, '-')
.replace(/\//g, '_');
const code_challenge = crypto.createHash('sha256')
.update(code_verifier)
.digest('base64')
.replace(/=/g, '')
.replace(/\+/g, '-')
.replace(/\//g, '_');
// Create the JWT payload with authorization request parameters and PKCE
const requestObject = jwt.sign(
{
iss: client_id, // Issuer - your client ID
aud: 'https://sample-authz.com/', // Audience - authorization server URL
client_id: client_id,
response_type: "code", // OAuth2 response type
scope: "openid profile", // Requested scopes
redirect_uri: "https://sample-client.com/callback", // Redirect URI
nonce: nonce, // Random nonce for replay protection
code_challenge: code_challenge, // PKCE code challenge
code_challenge_method: 'S256' // PKCE method (S256)
},
privateKey,
{
keyid: '[YOUR KID]', // Key ID of your public key
algorithm: 'RS256', // Signing algorithm
header: {
type: 'oauth-authz-req+jwt' // JWT type header for JAR
}
}
);
console.log('JWT:', requestObject);
console.log('Code verifier (store this for later):', code_verifier);
If you have node installed, you can copy the above code into a .js
file. You
will need to install the jsonwebtoken
library, using the npm install jsonwebtoken
command within the same directory where your JS file is. Then,
replace the variables and run it locally using the node <filename>
command.
Sample Output
JWT: eyJhbGciOiJSUzI1NiIsInR5cCI6Im9hdXRoLWF1dGh6LXJlcStqd3QiLCJraWQiOiJbWU9VUiBLSURdIn0.eyJpc3MiOiJbWU9VUiBDTElFTlQgSURdIiwiYXVkIjoiaHR0cHM6Ly9zYW1wbGUtYXV0aHouY29tLyIsImNsaWVudF9pZCI6IltZT1VSIENMSUVOVCBJRF0iLCJyZXNwb25zZV90eXBlIjoiY29kZSIsInNjb3BlIjoib3BlbmlkIHByb2ZpbGUiLCJyZWRpcmVjdF91cmkiOiJodHRwczovL3NhbXBsZS1jbGllbnQuY29tL2NhbGxiYWNrIiwibm9uY2UiOiI4NGZjZjY2ODRmNDZkYzYwMzU5MzlmOTI3MzA3ODQ5YyIsImNvZGVfY2hhbGxlbmdlIjoib1FtTmpjSkhsc0pFN2h2YlY4RTQyZm5wSlZMM01TSkg2RDd4b3VWeXBCWSIsImNvZGVfY2hhbGxlbmdlX21ldGhvZCI6IlMyNTYiLCJpYXQiOjE3NTA0MjMyMjZ9.DB-Dm2Xffhz0NV56nt9hb4jkHEXNy_Hl93DXAwrZ1oPOHxlfGfAqXnTGm3kfwwJkCz-qhQvRGsBfcGklBxfDqXBm3XBScEj2eBnjSj405PtT7YS89pRPOGmj8eukJvNfNN64p3wDIq2TYBnc1fQcjfgBZz4sTiAvNFyVAejOcAjwVkpV9EIlGJXAm7ynXexsVfYUfmO4iJGPMG7l7Wd3PoCT2ez8xVsAbC_iL33lt7guvdMQnnMuqU4J1aFkagchMYdGZugXMcoaGdVMM0lDXNbjCu416dZ7-r1kPvwEdtE_en13wImMTVSxmTKTAEP22Iuw2jfmjW6fR965nbCCVQ
Code verifier (store this for later): gRmhP7ItETpvmb9kSIz13P89q4aMSZxK5yq8tG4I2Qg
This is the result of running the script from the previous section. The JWT shown is the JWT-secured authorization request (JAR)—a signed request object that can be passed to the authorization server as part of the OAuth 2.0 authorization request.
The code_verifier
is part of the PKCE (Proof Key for Code Exchange) flow. You
must store it securely because it will be required later when exchanging the
authorization code for tokens at the token endpoint. The server uses it to
verify that the request and token exchange came from the same client instance.
Sample Output Decoded
Decoded Header
{
"alg": "RS256",
"typ": "oauth-authz-req+jwt",
"kid": "[YOUR KID]"
}
Decoded Payload
{
"iss": "[YOUR CLIENT ID]",
"aud": "https://sample-authz.com/",
"client_id": "[YOUR CLIENT ID]",
"response_type": "code",
"scope": "openid profile",
"redirect_uri": "https://sample-client.com/callback",
"nonce": "84fcf6684f46dc6035939f927307849c",
"code_challenge": "oQmNjcJHlsJE7hvbV8E42fnpJVL3MSJH6D7xouVypBY",
"code_challenge_method": "S256",
"iat": 1750423226
}
What Is JWT Secured Authorization Response Mode (JARM)?
In a standard OAuth 2.0 authorization response, parameters such as the code, state, or error messages are returned to the client via the browser—typically as query parameters or URL fragments. While this method is simple and widely used, it exposes the response to tampering, spoofing, or leakage, especially in environments involving browser extensions, proxies, or malicious scripts.
JWT Secured Authorization Response Mode (JARM) improves this by encapsulating the entire response in a digitally signed JWT. Instead of delivering raw values in the URL, the authorization server issues a signed JWT containing the response parameters, allowing the client to verify the integrity and origin of the message.
With JARM, the authorization response becomes a trusted, tamper-proof message—no longer just a collection of vulnerable URL parameters.
For scenarios that demand confidentiality, the JWT can also be encrypted, ensuring that only the intended client can read the response. This is particularly valuable when sensitive authorization data must be protected from browser or network-level exposure.
How JARM Works
-
Client requests JARM response:
- The client specifies a JARM-compatible
response_mode
, likequery.jwt
,fragment.jwt
, orform_post.jwt
.
- The client specifies a JARM-compatible
-
JWT is created:
-
The authorization server wraps parameters (
code
,state
, etc.) inside a JWT. -
Claims like
iss
(issuer),aud
(audience), andexp
(expiration) are added.
-
-
JWT is signed and, optionally, encrypted:
-
The JWT is signed using the server’s private key.
-
Optionally encrypted for confidentiality.
-
-
Client Validates JWT:
-
Signature is checked.
-
Claims are verified (
iss
,aud
,exp
). -
If valid, the client extracts the response and continues the flow.
-
Example: With and Without JARM
Standard OAuth Response
https://sample-client.com/app/callback?code=AUTH_CODE&state=STATE
JARM Secured Response
https://sample-client.com/app/callback?response=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...
Decoded JWT Payload
{
"iss": "https://sample-authz.com",
"aud": "web-client",
"exp": 1657190372,
"code": "AUTH_CODE",
"state": "STATE"
}
Side-by-Side Security Comparison
Feature | Without JAR/JARM | With JAR & JARM |
---|---|---|
Format | Plain query parameters | Signed JWTs |
Request Integrity | ❌ No | ✅ Yes |
Response Integrity | ❌ No | ✅ Yes |
Confidentiality | ❌ No | ✅ Optional (via encryption) |
Source Authentication | ❌ No | ✅ Yes (with iss and signature) |
Replay Protection | ❌ No | ✅ Yes (exp claim) |
Mix-Up Attack Defense | ❌ No | ✅ Yes (via iss claim) |
Why JAR and JARM Matter for API Security
In any system where APIs handle sensitive data or user authorization, securing the full OAuth 2.0 flow is essential. Standard OAuth requests and responses are often exposed to the user’s browser or passed through untrusted intermediaries, leaving them vulnerable to tampering, interception, or redirection.
Implementing JWT Secured Authorization Request (JAR) and JWT Secured Authorization Response Mode (JARM) significantly reduces these risks by applying modern cryptographic protections to the most critical parts of the authorization flow:
-
End-to-end trust
JAR and JARM ensure that both the authorization request and the response are verifiably authentic and have not been altered in transit. This creates a consistent trust boundary from client to server and back.
-
Mitigation of common OAuth vulnerabilities
Wrapping request and response parameters in signed (and optionally encrypted) JWTs helps prevent attacks such as parameter injection, token leakage, response spoofing, and mix-up attacks that can compromise the integrity of the flow.
-
Alignment with modern security standards
JAR and JARM reflect best practices in API security by providing integrity, authenticity, and optional confidentiality. They are increasingly required or recommended in environments where strong assurance and auditability are essential.
Whether you're building public-facing applications, securing internal services, or enabling third-party access, JAR and JARM provide the cryptographic guarantees that OAuth 2.0 alone does not.
Final Thoughts
JAR and JARM are essential building blocks for securing OAuth 2.0 flows in any environment where API integrity and trust matter. By turning authorization requests and responses into signed (and optionally encrypted) JWTs, you prevent tampering, ensure verifiable intent, and safeguard against a wide range of OAuth-based attacks.
If you operate an authorization server or share data through APIs, it is critical to:
-
Support and enforce JAR and JARM on your server.
-
Provide your clients—whether they are customers, partners, or third-party developers—with the necessary keys, certificates, and metadata to sign and validate JWTs securely.
-
Embed JAR and JARM into your developer documentation and onboarding workflows, so adopters can implement them correctly from day one.
These standards aren’t just about regulatory compliance—they’re foundational for building scalable, tamper-proof, and future-ready identity and data-sharing ecosystems.
To learn more about the current state of API security and best practices for hardening your integrations, download our comprehensive API Security Report.
API Security Report
Helping Enterprises Recognize and Address Critical Risks
More than 80% of organizations are exposing sensitive data with weak API security
Download Now →