Protecting an authorization response
With PAR and JAR, the printer's request reaches the photo service intact and from a client the service can identify. The answer still comes back the old way. The photo service redirects your browser to the printer's callback with a code, state, and iss in the query string, and the printer reads them from the address your browser requests.
Everything the previous groups said about requests in the browser applies to that response too.
An answer anyone can write
The printer's callback is an ordinary web address. A request can reach it from the photo service's genuine redirect, from a link someone sent you, or from a browser extension that rewrote the address on the way. The parameters are plain text. Nothing in them shows that the photo service produced them, or that they arrived unchanged.
The printer is not defenseless. The authorization code flow lessons gave it three checks, which Correlating requests and responses set side by side: state ties the response to a pending attempt in your session, PKCE ties the code to a verifier only the printer holds, and iss names the server that is supposed to have answered. Each answers one question well. None of them shows that the response as a whole came, unaltered, from the photo service and was meant for the printer. Even iss is just text that the response claims about itself.
For many deployments those checks are enough. A deployment that wants the response itself to be verifiable, the way a signed request object is, needs the authorization server to sign it.
Signing the response
The JWT Secured Authorization Response Mode for OAuth 2.0, known as JARM, is an OpenID Foundation specification that does exactly that. Instead of placing each response parameter in the redirect separately, the authorization server puts all of them into a JWT, signs it with its own private key, and sends the JWT as a single parameter called response.
The name comes from response modes. A response mode decides how an authorization server encodes the response it sends back through the browser. The default for the code flow puts the parameters in the query string, and other modes use the URL fragment or a form post. JARM adds a JWT version of each.
Alongside the usual parameters, every JARM response carries three claims that protect it. iss names the authorization server that created it. aud names the client it is meant for, by client ID. exp limits how long it is valid, and the specification recommends no more than ten minutes. The printer verifies the signature with a key from the photo service's published key set at https://auth.photos.example/jwks.
A verified response gives the printer four assurances. The parameters are exactly as the photo service wrote them, because any change breaks the signature. The photo service produced them, because only it holds the signing key. They were addressed to the printer, so a response issued to another client is refused. And the server that answered is named inside the signature, which protects against the mix-up attacks that the iss parameter also addresses.
Encrypting the response
A signed response is still readable. Anyone who sees the redirect, or finds it later in history or a log, can decode the JWT and read the code inside it. JARM therefore allows the server to encrypt the signed JWT as well, using a public encryption key the client has registered, so that only the client can open it. As with request objects, the order is sign first, then encrypt.
Encryption hides what the response says, not the response itself. An encrypted response that leaks can still be copied and delivered again as a whole, so it does not remove the need for PKCE. Many deployments that sign their responses do not encrypt them. A code-only response contains nothing that PKCE does not already protect, and every extra layer is another place for two implementations to disagree. The FAPI 2.0 Message Signing profile, for example, requires signed responses but recommends against encrypting them, for interoperability.
What a signature cannot tell
A signature proves who wrote a response and for which client. It cannot prove which browser session the response belongs to.
Return to the forged callback from Request correlation and CSRF, this time with JARM in place. The attacker connects their own photo account in their own browser and stops just before the callback. The photo service has produced a perfectly genuine, signed response: issued by the photo service, addressed to photo-printer, unexpired. When the attacker gets your browser to deliver it to the printer, every JARM check passes. What stops the attack is what stopped it before: the state inside does not match a pending attempt in your session, and the code cannot be exchanged without the attacker's verifier.
So JARM adds to the printer's checks rather than replacing any of them. It also adds less evidence than the signed request did. A signed request records what a client asked for, such as a payment of 42.50 EUR. A signed response records a code and a state, values that only mean something inside one protocol exchange.
That balance explains where JARM is used. As with request objects, the FAPI profiles moved from requiring signed responses to offering them. FAPI 1.0 Advanced required them. The FAPI 2.0 Security Profile keeps the response to a code protected by PKCE and the iss parameter, and leaves JARM to its message signing profile for deployments that want every message signed.