JAR & JARM
このコンテンツはまだ日本語訳がありません。
Overview
Section titled “Overview”JAR (JWT Secured Authorization Request, RFC 9101) and JARM (JWT Secured Authorization Response Mode) provide end-to-end integrity protection for the authorization flow:
- JAR — Authorization parameters are sent as a signed JWT (Request Object), preventing parameter tampering
- JARM — The authorization response is returned as a signed JWT, preventing response tampering
Together, they ensure that neither the authorization request nor the response can be modified by an attacker.
JAR (JWT Secured Authorization Request)
Section titled “JAR (JWT Secured Authorization Request)”How JAR Works
Section titled “How JAR Works”Instead of passing authorization parameters as query string values, the client creates a signed JWT containing all parameters and sends it in the request parameter:
Standard: /authorize?client_id=app&scope=openid&redirect_uri=...With JAR: /authorize?client_id=app&request=eyJhbGciOiJSUzI1NiJ9...The JWT is signed with the client’s private key, ensuring the authorization server can verify that the parameters have not been modified.
JARBuilder
Section titled “JARBuilder”Create Request Objects using JARBuilder:
import { JARBuilder } from '@authrim/core';
const jarBuilder = new JARBuilder({ signJwt: async (header, claims) => { // Sign the JWT with your private key return signedJwtString; }, keyId: 'my-key-id', algorithm: 'RS256', lifetime: 300, // seconds});JARBuilderConfig
Section titled “JARBuilderConfig”| Parameter | Type | Default | Description |
|---|---|---|---|
signJwt | (header, claims) => Promise<string> | Required | JWT signing function |
keyId | string | — | Key ID (kid) for the JWT header |
algorithm | string | 'RS256' | Signing algorithm |
lifetime | number | 300 | Request Object lifetime in seconds |
Building a Request Object
Section titled “Building a Request Object”const requestObject = await jarBuilder.buildRequestObject({ clientId: 'my-app', issuer: 'https://auth.example.com', redirectUri: 'https://myapp.com/callback', scope: 'openid profile email', state: generatedState, nonce: generatedNonce, codeChallenge: generatedChallenge, codeChallengeMethod: 'S256',});
// requestObject is a signed JWT stringJARRequestOptions
Section titled “JARRequestOptions”| Parameter | Type | Description |
|---|---|---|
clientId | string | OAuth 2.0 client ID |
issuer | string | Authorization server issuer URL (used as aud) |
redirectUri | string | Redirect URI |
scope | string | Requested scopes |
state | string | CSRF protection state |
nonce | string | Replay protection nonce |
codeChallenge | string | PKCE code challenge |
codeChallengeMethod | string | Challenge method (always 'S256') |
responseType | string | Response type (default: 'code') |
prompt | string | Prompt behavior |
loginHint | string | Login hint |
acrValues | string | Authentication context |
extraClaims | Record<string, unknown> | Additional JWT claims |
Request Object JWT Claims
Section titled “Request Object JWT Claims”The generated JWT includes:
| Claim | Description |
|---|---|
iss | Client ID |
aud | Authorization server issuer URL |
iat | Issued at timestamp |
exp | Expiration timestamp |
jti | Unique identifier |
nbf | Not before timestamp |
response_type | OAuth response type |
client_id | Client identifier |
redirect_uri | Redirect URI |
scope | Requested scopes |
state | CSRF protection value |
nonce | Replay protection value |
code_challenge | PKCE code challenge |
code_challenge_method | PKCE method |
Using JAR with Authorization Requests
Section titled “Using JAR with Authorization Requests”Build the signed request object with JARBuilder, then add it to the authorization request according to your application flow:
const requestObject = await jarBuilder.buildRequestObject({ clientId: 'my-app', issuer: 'https://auth.example.com', redirectUri: 'https://myapp.com/callback', scope: 'openid profile', state, nonce, codeChallenge, codeChallengeMethod: 'S256',});
const url = `${discovery.authorization_endpoint}?client_id=my-app&request=${encodeURIComponent(requestObject)}`;Checking If JAR Is Required
Section titled “Checking If JAR Is Required”import { isJarRequired } from '@authrim/core';
const discovery = await client.discover();
if (isJarRequired(discovery)) { // Server requires signed request objects. // Use JARBuilder to create the request object before redirecting.}The isJarRequired() function checks the require_signed_request_object field in the discovery document.
JARM (JWT Secured Authorization Response Mode)
Section titled “JARM (JWT Secured Authorization Response Mode)”How JARM Works
Section titled “How JARM Works”Instead of returning authorization parameters as query string values, the authorization server returns a signed JWT in the response parameter:
Standard: /callback?code=abc&state=xyzWith JARM: /callback?response=eyJhbGciOiJSUzI1NiJ9...The JWT is signed by the authorization server, ensuring the client can verify that the response has not been tampered with.
JARMValidator
Section titled “JARMValidator”Validate JARM responses using JARMValidator:
import { JARMValidator } from '@authrim/core';
const jarmValidator = new JARMValidator({ verifyJwt: async (jwt, issuer) => { // Verify the JWT signature using the server's public key return decodedClaims; },});Validating a JARM Response
Section titled “Validating a JARM Response”// Extract the response parameter from the callback URLconst responseJwt = JARMValidator.extractResponseFromCallback(callbackUrl);
if (responseJwt) { const result = await jarmValidator.validateResponse(discovery, responseJwt, { expectedState: storedState, clientId: 'my-app', clockSkewSeconds: 60, });
console.log(result.code); // Authorization code console.log(result.state); // Validated state}JARMValidationOptions
Section titled “JARMValidationOptions”| Parameter | Type | Default | Description |
|---|---|---|---|
expectedState | string | Required | Expected state value for CSRF validation |
clientId | string | Required | Client ID (must match aud claim) |
clockSkewSeconds | number | 60 | Allowed clock skew in seconds |
JARMValidationResult
Section titled “JARMValidationResult”| Property | Type | Description |
|---|---|---|
code | string | The authorization code |
state | string | The validated state parameter |
JARM Response Claims
Section titled “JARM Response Claims”The JWT returned by the server contains:
| Claim | Description |
|---|---|
iss | Authorization server issuer |
aud | Client ID |
exp | Expiration timestamp |
code | Authorization code (on success) |
state | State parameter |
error | Error code (on failure) |
error_description | Error description (on failure) |
Complete Example: JAR + JARM Flow
Section titled “Complete Example: JAR + JARM Flow”import { JARBuilder, JARMValidator, isJarRequired } from '@authrim/core';
// Setupconst jarBuilder = new JARBuilder({ signJwt: mySignFunction, algorithm: 'RS256',});
const jarmValidator = new JARMValidator({ verifyJwt: myVerifyFunction,});
// Step 1: Build authorization URL with JARconst requestObject = await jarBuilder.buildRequestObject({ clientId: 'my-app', issuer: 'https://auth.example.com', redirectUri: 'https://myapp.com/callback', scope: 'openid profile', state: generatedState, nonce: generatedNonce, codeChallenge: generatedChallenge, codeChallengeMethod: 'S256',});
const authUrl = `${discovery.authorization_endpoint}?client_id=my-app&request=${requestObject}`;
// Step 2: After redirect, validate JARM responseconst responseJwt = JARMValidator.extractResponseFromCallback(callbackUrl);
if (responseJwt) { const { code, state } = await jarmValidator.validateResponse( discovery, responseJwt, { expectedState: storedState, clientId: 'my-app' }, );
// Step 3: Exchange code for tokens const tokens = await exchangeCode(code);}Error Handling
Section titled “Error Handling”| Error Code | Description | Recovery |
|---|---|---|
jar_signing_error | Failed to sign the Request Object | Check signing key and configuration |
jar_required | Server requires JAR but it wasn’t used | Set useJar: true |
jarm_validation_error | JARM response validation failed | Check server configuration |
jarm_signature_invalid | JARM JWT signature verification failed | Verify server keys |
References
Section titled “References”- RFC 9101 — The OAuth 2.0 Authorization Framework: JWT-Secured Authorization Request (JAR)
- Financial-grade API Security Profile 2.0
Next Steps
Section titled “Next Steps”- DPoP — Bind tokens to cryptographic keys
- PAR — Push authorization parameters via back-channel
- Authorization Code Flow — Standard login flow