Skip to main content

Securing Authorization Requests and Responses with JAR and JARM

· 9 min read
Wojciech Kotłowski
Senior Technical Writer

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.

Standard Authorization Request - exposed to tampering, manipulation, or leakage

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.

JWT-Secured Authorization Request - boosting the security

With JAR, the authorization request becomes a secure, verifiable message—no longer just a set of exposed URL parameters.

Need additional protection?

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

  1. Key Setup:

    • The client generates a public/private key pair.

    • The public key is registered with the authorization server.

  2. Create Request Object:

    • Authorization parameters like client_id, redirect_uri, scope, and state are placed into a JWT.

    • The JWT is signed using the client’s private key.

  3. 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.


      info

      It is possible to use JAR when sending a request to the OAuth Pushed Authorization Request (PAR) endpoint.

  4. 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 random code_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.

→ Need keys to run the above? Request Raidiam Trust Platform Sandbox Access!

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.

Standard Authorization Response

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.

JWT Secured Authorization Response Mode

With JARM, the authorization response becomes a trusted, tamper-proof message—no longer just a collection of vulnerable URL parameters.

Need additional protection?

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

  1. Client requests JARM response:

    • The client specifies a JARM-compatible response_mode, like query.jwt, fragment.jwt, or form_post.jwt.
  2. JWT is created:

    • The authorization server wraps parameters (code, state, etc.) inside a JWT.

    • Claims like iss (issuer), aud (audience), and exp (expiration) are added.

  3. JWT is signed and, optionally, encrypted:

    • The JWT is signed using the server’s private key.

    • Optionally encrypted for confidentiality.

  4. 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

FeatureWithout JAR/JARMWith JAR & JARM
FormatPlain query parametersSigned 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:

  1. Support and enforce JAR and JARM on your server.

  2. 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.

  3. 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 →