Join our Newsletter — 33% off our NHI Course
Home› Guides› JARM Explained: How Signed OAuth Authorisation Responses Protect…
Guide Authentication

JARM Explained: How Signed OAuth Authorisation Responses Protect High-Assurance Clients

PDF for newsletter subscribers · free to read online

← All guides
By NHI Mgmt Group, edited by Lalit Choda Updated 12 November 2026 12 min read
On this page

A classic OAuth authorisation response is a handful of unsigned query parameters that travel through the user's browser, so the client has no cryptographic proof of who issued the code, who it was meant for, or whether anyone changed it on the way. JARM, the JWT Secured Authorization Response Mode, wraps that response in a signed (and optionally encrypted) JWT from the authorisation server. It is one of the two response protection options in FAPI 1.0 Advanced, which many open banking ecosystems built on, and it is the mechanism for signed responses in the FAPI 2.0 Message Signing profile. This guide explains how JARM works, how it differs from request signing, how to configure and validate it, and where it fits next to PKCE and the iss response parameter. It is for identity engineers building or integrating high-assurance OAuth clients and authorisation servers. For signing the request side, see the OAuth Pushed Authorization Requests and JAR Guide.

Key takeaways

  • JARM protects the response, JAR protects the request. They solve different problems and high-assurance profiles often want both.
  • Four response modes. query.jwt, fragment.jwt, form_post.jwt and the shortcut jwt, with the JWT carried in a single parameter named response.
  • Three claims make it work. iss, aud and exp let the client detect mix-up, misdirected responses and old responses; the specification recommends a lifetime of no more than 10 minutes.
  • Validation order matters. Check the issuer before fetching keys, reject the none algorithm, and do not touch the code or other response parameters until every check passes.
  • It does not replace PKCE. JARM's own security considerations still call for PKCE, because a signed code can still leak.

What JARM is and the problem it solves

JARM is an OpenID Foundation specification produced by the FAPI working group, published as a final specification in November 2022. It defines new response modes that tell the authorisation server to return the authorisation response as a JWT instead of separate parameters.

In the standard authorisation code flow, the server redirects the browser to the client with code and state in the URL. Those values are not integrity protected. A client that talks to several authorisation servers cannot prove which one issued a code, which is the root of mix-up attacks, and an attacker who can inject or alter a response in the browser leaves no cryptographic trace. JARM changes that by putting every response parameter, including errors, inside a JWT signed by the authorisation server and addressed to the client.

MechanismWhat it protectsDirectionDefined in
JAR (signed request object)Integrity and origin of the authorisation requestClient to serverRFC 9101
PARMoves the request to a back-channel POST, returns a request_uriClient to serverRFC 9126
JARMIntegrity, origin, audience and freshness of the authorisation response; optional confidentialityServer to clientOpenID Foundation JARM
ID Token as detached signatureHashes of code (c_hash) and state (s_hash) signed in an ID Token returned with the responseServer to clientFAPI 1.0 Part 2 with OpenID Connect
iss response parameterIdentifies the issuer, unsigned, to detect mix-upServer to clientRFC 9207
PKCEBinds the code to the client instance that started the flowBoth, checked at token endpointRFC 7636

How JARM works

The client asks for a JWT response by setting response_mode in the authorisation request. The server builds a JWT containing the normal response parameters plus three claims, signs it, optionally encrypts it, and returns it in a parameter named response.

Response modes

response_modeHow the JWT is deliveredNotes
query.jwtIn the redirect URI query stringMust not be used with token or id_token response types unless the JWT is encrypted
fragment.jwtIn the redirect URI fragmentNot sent to the server in the HTTP request; client-side code reads it
form_post.jwtAuto-submitted HTTP POST to the redirect URIKeeps the JWT out of URLs, browser history and referrer headers
jwtThe default encoding for the response typequery.jwt for code; fragment.jwt for token and OpenID Connect response types other than none

JWT contents

  • iss: the authorisation server's issuer identifier.
  • aud: the client_id of the intended client.
  • exp: expiry; the specification recommends a maximum lifetime of 10 minutes.
  • Response parameters: code, state, error and error_description, and any extension parameters such as session_state, all inside the JWT rather than beside it.

The JWT is either signed, or signed then encrypted as a Nested JWT. The server picks the algorithms, normally from the client's registered metadata.

Because errors also travel inside the JWT, a client can trust an error response as much as a success response: an attacker cannot forge an access_denied or invalid_request to confuse the client's state. Encryption adds confidentiality on top of integrity, which matters when the front channel would otherwise expose personal data, but JARM Section 5.4 notes that if the encryption is ever broken the whole response is exposed, so encryption is a complement to PKCE, not a substitute.

Configuration: client and server metadata

MetadataWhereMeaningDefault or rule
authorization_signed_response_algClient registrationSigning algorithm for responsesRS256 if omitted; none is not allowed
authorization_encrypted_response_algClient registrationKey management algorithm for encryptionNo encryption if omitted
authorization_encrypted_response_encClient registrationContent encryption algorithmA128CBC-HS256 when an alg is set; requires authorization_encrypted_response_alg
response_modes_supportedServer metadataAdvertised response modesShould include the four JWT modes
authorization_signing_alg_values_supportedServer metadataSigning algorithms the server can usePublish only those your profile allows
authorization_encryption_alg_values_supported and authorization_encryption_enc_values_supportedServer metadataEncryption algorithms the server can usePublish only if you encrypt responses

Profiles override the defaults. FAPI 1.0 Part 2 Section 8.6 requires PS256 or ES256, says RSASSA-PKCS1-v1_5 algorithms such as RS256 should not be used, forbids none, and forbids RSA1_5 for JWE. A FAPI client should therefore register PS256 or ES256 explicitly rather than rely on the JARM default of RS256.

Validating a JARM response

JARM Section 2.4 sets out the client's checks. Treat them as a fixed sequence in a single, well-tested library function:

  1. Decrypt the JWT if the client registered for encryption, and reject an unencrypted response when encryption is expected.
  2. Check iss against the issuer the client sent this user to. Do this before fetching any keys; Section 5.1 warns that crafted JWTs can otherwise make the client fetch keys from attacker-chosen locations.
  3. Check aud equals the client's own client_id.
  4. Check exp has not passed, with only a small clock skew allowance.
  5. Verify the signature with the issuer's published keys, using only the expected algorithm. Reject none.
  6. Only then read code, state and error, check state against the browser session, and redeem the code with the PKCE verifier.

The specification leaves CSRF detection to the OAuth security best current practice, so state or PKCE still has to tie the response to the user's session. OWASP ASVS 5.0 V10.1.2 captures the same principle: accept values only from a flow started by the same user agent session and transaction. See the OAuth 2.0 Security Best Current Practice (RFC 9700) Guide.

Where profiles require JARM

  • FAPI 1.0 Part 2 Advanced (Final, 12 March 2021): Section 5.2.2 requires the authorisation server to support either the code id_token response type, where the ID Token acts as a detached signature with c_hash and s_hash, or the code response type with response_mode=jwt. With JARM and the openid scope, the ID Token comes from the token endpoint instead. Public clients are not supported.
  • FAPI 2.0 Message Signing: Section 5.4 uses JARM to sign authorisation responses for non-repudiation of front-channel responses. Where an implementation adopts response signing, the server shall support, require and issue signed responses, and the client shall set response_mode=jwt and verify the responses. The profile notes that the RFC 9207 iss parameter should only appear inside the JWT, and Section 6.6 warns that signed codes and state values have limited non-repudiation value on their own. See the FAPI 2.0 Security Profile Implementation Guide.
  • Open banking ecosystems: many national schemes adopted FAPI 1.0 Advanced, so their clients meet JARM or the detached signature pattern. Check your ecosystem's own profile for which one it mandates. See the Open banking and open finance security guide.

Decision guidance

SituationChoiceWhy
FAPI 1.0 Advanced client without OpenID Connect needscode with response_mode=jwtPlain OAuth works; no ID Token needed in the front channel
FAPI 1.0 Advanced client already processing ID Tokenscode id_token (detached signature) or JARMBoth are allowed; JARM keeps personal data out of the front channel by moving the ID Token to the token endpoint
FAPI 2.0 Security Profile onlyPKCE, PAR and the iss parameter; JARM optionalFAPI 2.0 baseline does not need signed responses; add Message Signing only where non-repudiation is required
Client talks to many authorisation serversJARM or at least RFC 9207 issSigned iss and aud give strong mix-up protection
Response carries sensitive data or uses query.jwt with tokensSigned then encrypted JARM, prefer form_post.jwtEncryption and POST delivery limit leakage in URLs and logs
General web or mobile app, single providerPKCE and iss; JARM usually not neededLower complexity for limited added benefit

Small teams should take JARM from their authorisation server and client library rather than build it. Large platforms serving many relying parties should publish JARM support in metadata so high-assurance clients can opt in without custom work.

Implementing JARM: a phased approach

  1. Confirm the requirement. Read your ecosystem profile to see whether JARM, the detached signature pattern or neither is required, and which algorithms are allowed.
  2. Prepare keys. Publish the server's signing keys in its JWKS with key IDs, and plan rotation with overlap so clients can fetch new keys before old ones are retired. For encryption, collect each client's encryption keys from its registered JWKS.
  3. Register clients. Set authorization_signed_response_alg to the profile's algorithm (for FAPI, PS256 or ES256) and, if needed, the encryption metadata.
  4. Build validation once. Implement the six-step check in one library function, with tests for wrong iss, wrong aud, expired exp, bad signature, none algorithm, unexpected algorithm and unencrypted responses when encryption is registered.
  5. Test against conformance tools. The OpenID Foundation runs conformance suites for FAPI profiles; use them in a test environment before production.
  6. Roll out and roll back. Enable response_mode=jwt per client. Rollback is removing the response_mode from the client's request, as long as the server still allows plain responses for that client during the transition.

Detection and measurement

Log each response validation failure with the reason (iss, aud, exp, signature, algorithm, decryption). A rise in iss or aud failures can indicate mix-up or injection attempts; signature failures after a key rotation usually mean a client has cached old keys.

MetricTarget
High-assurance clients using response_mode=jwt or the profile's alternative100 percent
Clients registered with an algorithm outside the profile listZero
Validation failures by reason per weekLow and explained; spikes investigated
JARM responses with exp beyond 10 minutesZero
Days from server key rotation to zero signature failuresWithin the planned overlap window

Framework mapping

FrameworkReferenceRelevance
NIST SP 800-53 Rev. 5SC-8, SC-23Integrity of the response in transit and session authenticity
NIST SP 800-53 Rev. 5SC-12, SC-13Key management and approved algorithms for signing and encryption
NIST SP 800-53 Rev. 5SI-10Validating inputs (the response JWT) before use
NIST CSF 2.0PR.DS-02, PR.AA-03Data in transit protected; services authenticated
OWASP ASVS 5.0V10.1.2, V10.2.2Accept only values from the client's own flow; defend against mix-up
MITRE ATT&CKT1557, T1528Interception or tampering of responses; theft of codes and tokens

Common failure patterns

  • Reading the code before validating. Some integrations parse the JWT payload, take the code and validate later or never, which removes all the protection.
  • Accepting any algorithm. Libraries that trust the JWT header's alg value can be led to accept none or an unexpected algorithm. Pin the algorithm per client.
  • Fetching keys before checking the issuer. Looking up keys for whatever issuer an unchecked JWT names, which Section 5.1 warns can be abused for denial of service. Resolve keys only for the issuer the client expects.
  • Relying on the RS256 default. Registering no algorithm in a FAPI ecosystem leaves the JARM default of RS256, which FAPI 1.0 Part 2 says should not be used.
  • Dropping PKCE. Treating a signed response as enough; JARM Section 5.4 notes a code can still leak, so PKCE stays.
  • Tokens in query.jwt without encryption. Explicitly disallowed by the specification and a leakage risk through logs and referrers.

Practitioner checklist

  • Confirm whether your profile needs JARM, the detached signature pattern or neither.
  • Register an explicit signing algorithm per client; PS256 or ES256 for FAPI.
  • Publish response_modes_supported and the JARM algorithm metadata on the server.
  • Keep JARM lifetimes at 10 minutes or less.
  • Validate in order: decrypt, iss, aud, exp, signature, then parameters.
  • Reject none and any algorithm not registered for the client.
  • Keep PKCE and state checks alongside JARM.
  • Prefer form_post.jwt, and encrypt when responses carry sensitive data.
  • Rotate signing keys with overlap and monitor signature failures.
  • Test with the OpenID Foundation conformance suite before go-live.

Standards and references

Related NHI Mgmt Group resources: OAuth Pushed Authorization Requests and JAR Guide · FAPI 2.0 Security Profile Implementation Guide · OAuth 2.1, GNAP and FAPI Guide · Open banking and open finance security

PDF for subscribers

Take this guide with you

Every section, table and checklist in one PDF, with links to the sources and related guides. Subscribe to our weekly NHI & AI Identity Journal and we’ll email you a download link.

Free weekly newsletter

Subscribe to the NHI & AI Identity Journal

The latest on NHI and Agentic AI security – articles, research, breaches, news and events every week.

Bonus 33% off our NHI Course when you subscribe.

NHIMG Editorial Note
Researched and written by the NHI Mgmt Group editorial team using AI tools, under the editorial direction of Lalit Choda. Facts are drawn from the sources listed on this page and checked by our automated quality process. Last updated 12 November 2026. Spotted an error? Use our contact page.
NHI Mgmt Group, the independent authority on Non-Human Identity, IAM and Agentic AI security. nhimg.org

PDF Downloads for Newsletter Subscribers

Get the guide

Enter your details and we’ll email you a download link.

The full guide is free to read online. The PDF is a thank you for subscribing.

Fill in all fields marked * and tick the PDF box to continue.