Beta

Create a tenant

A new tenant starts with its own users, OAuth settings, audit history and logs. You are its first Tenant Admin.

BTL Admin

Validating a request object

Your browser arrives at the photo service carrying client_id=photo-printer and a request object. Decoding the object would show a scope, a return address, and everything else the printer asked for. None of it means anything yet. Until the photo service has established that the object is genuine, current, and meant for it, those claims are only claims.

Before any parameter is used

The photo service works through the object in an order that keeps it from relying on anything it has not checked:

  1. Identify the client. Look up the registration for the client_id in the URL. Exactly one of request and request_uri may be present.
  2. Obtain the object. Take it from request, or fetch it from request_uri under the precautions described below.
  3. Decrypt it if it is encrypted, using the photo service's own private key. If decryption fails, stop.
  4. Verify the signature. The key must belong to this client. If the header names a key with kid, that must be the key used, and it must be one registered for photo-printer, either directly or in the key set the printer registered. The algorithm must be one the service allows for this client. An object whose alg is none carries no signature and proves nothing, and a service that requires signed objects must refuse it.
  5. Check the claims that protect the object. Under the ordinary JWT rules, an aud that is present must name the photo service's issuer identifier, an exp must not have passed, and an nbf must have arrived. RFC 9101 asks only that a signed object include iss and aud, so the rest is the service's policy, or the policy of a profile it follows: which claims must be present, whether iss must equal the client ID, the longest lifetime it accepts, whether typ must be oauth-authz-req+jwt, and whether it tracks jti values to refuse repeats.
  6. Match the client. The client_id claim inside the object must be identical to the client_id in the URL.
  7. Validate the request. Only now read the authorization parameters and check them as for any authorization request: the return address against the registration, the scope against what the printer may request, the PKCE challenge, and the rest.

The last step is easy to forget once a signature has verified. The signature proves that the printer wrote the request. It does not make the request acceptable. A correctly signed object asking for photos.delete is refused for the same reason an unsigned one would be: the printer's registration does not allow that scope.

The key check in step 4 deserves attention too. The service holds public keys for many clients. Verifying the printer's request with whichever key happens to make the signature valid would let any client sign requests that name the printer. The key has to come from the printer's own registration.

Only the object counts

All domains and values in these examples are fictional. A client may repeat parameters outside the object, for example to work with older servers, and someone may add parameters in the browser:

https://auth.photos.example/authorize
  ?client_id=photo-printer
  &request=eyJhbGciOiJFUzI1NiIsImtpZCI6InByaW50ZXItMjAyNi0xMCIsInR5cCI6Im9hdXRoLWF1dGh6LXJlcStqd3QifQ...
  &scope=photos.read%20photos.delete

The object is shortened here for display. The photo service uses only the parameters inside the object. The scope in the URL is ignored, even though it names a different value. That rule is what makes the signature meaningful. If one part of the service read the scope from the URL while another verified the signature over the object, an attacker could pair a genuine signed object with whatever unsigned values they liked.

client_id is the one value that must appear in both places, and step 6 requires the two to be identical for the same reason: a request should never be able to name one client in the URL while carrying another client's object.

OpenID Connect, which used request objects before JAR, follows a different rule. It assembles the request from both sources and lets the object's value win where they overlap. A server that supports both needs to be clear about which rule applies, and a client should not rely on values outside the object.

Fetching a request by reference

When the printer passes its object by reference, step 2 means the photo service sends a request of its own to an address it received from a browser. The request_uri value is not signed, because it travels in the URL, so malicious software in the browser could change it. That makes the fetch an opportunity for abuse. An attacker could point the service at an enormous or deliberately slow resource to tie it up, at another site the attacker wants to flood with requests, or at an address inside the service's own network. A reference could even point to another authorization request that carries its own reference, sending the service round in circles.

The service protects itself in a few ways. It fetches only from locations it expects for this client, such as addresses registered in advance. It checks that the response has the media type application/oauth-authz-req+jwt. It limits how long it waits and how much it will read. And it never follows a reference found inside what it fetched, which the rule against request_uri inside an object already forbids.

A fetched object is checked exactly like one sent by value. Retrieving it from printer.example over HTTPS does not replace the signature check, because it is the signature, not the address it came from, that shows the printer created it.

Errors and downgrades

JAR adds four error codes to those an authorization endpoint can return:

ErrorMeaning
invalid_request_objectThe object is invalid, for example because decryption or signature verification failed or the key does not belong to the client.
invalid_request_uriFetching the request_uri failed or returned invalid data.
request_not_supportedThe server does not accept the request parameter.
request_uri_not_supportedThe server does not accept the request_uri parameter.

These errors go back to the client as authorization error responses, but only to a return address the service can trust. A redirect_uri read from an object whose signature failed is not trustworthy. The service either uses an address it can establish from the registration alone or shows an error page, as for any request with a doubtful return address.

The most damaging failure is quieter. If the photo service verifies signed objects but also still accepts ordinary unsigned requests from the printer, an attacker can skip the signature entirely by sending an ordinary request. To close that route, a client registration or the server as a whole can set require_signed_request_object to true:

{
  "client_id": "photo-printer",
  "token_endpoint_auth_method": "private_key_jwt",
  "jwks_uri": "https://printer.example/oauth/jwks.json",
  "request_object_signing_alg": "ES256",
  "require_signed_request_object": true
}

The three entries after the client ID record the arrangement from Building a request object: client assertions, the key set that holds printer-2026-10, and the algorithm for request objects. The last entry closes the old route. With it, the photo service must reject any authorization request from the printer that does not carry a request object, and any object that uses alg none. As server metadata, the same setting applies to every client. Without it, signing is only an option the printer has chosen, and an option an attacker can decline.

Try it in the Lab

PUT IT INTO PRACTICE

Check your understanding

Try these questions before moving on. If an answer isn't right, use the feedback and try again.

0 of 2 answered correctly

Enable JavaScript to answer these questions and save progress in this browser.

QUESTION 1 OF 2A correctly signed request object from the printer asks for photos.delete, which the printer's registration does not allow. What happens?

QUESTION 2 OF 2The URL carries a valid request object and also scope=photos.read photos.delete outside it. Which scope does the service use?

We value your privacy

We use cookies and similar technologies to enhance your browsing experience, and analytics to understand our traffic. By clicking "Allow All", you consent to optional analytics. Cookie Policy

Learn identity