Using the request URI
The printer holds a request URI that will work for the next 90 seconds. It now sends your browser to the photo service's authorization endpoint, and the request your browser carries there is the shortest one in these lessons.
Sending the browser
All domains, codes, and identifiers in these examples are fictional. With its parameters on separate lines, the address is:
https://auth.photos.example/authorize
?client_id=photo-printer
&request_uri=urn%3Aietf%3Aparams%3Aoauth%3Arequest_uri%3Ademo-pushed-request-7
Compare it with the request in The authorization request lesson. There is no scope, no return address, no state, and no PKCE challenge. All of them are stored at the photo service, where the push left them. The request URI is encoded because it is a value inside another URL.
client_id is still required. It names the client whose request this is, and the service will check that it matches the client the reference was issued to. Anyone looking at the address bar can see that the printer wants to connect, but nothing there says what it asked for, and there is nothing to edit except a reference that only means something to the photo service.
What the server does with it
When your browser arrives, the photo service turns the reference back into a request:
- It looks up the stored request. An unknown reference is rejected.
- It checks that the reference has not expired and has not already been used.
- It checks that the
client_idin the address is the client that pushed the request. - It processes the stored parameters as an authorization request.
The last step can be shorter than usual. The service validated the request when it was pushed, and it may skip those checks now if it can tell that this is a pushed request and that nothing has changed since that would alter the result. Something can change in 90 seconds. If the printer's registration was suspended, or its return address removed, between the push and your visit, the service checks again and refuses.
Then the familiar part begins. You sign in if you need to, and the consent screen, built from the stored request, asks whether Photo Printer may view your photos. When you approve, the response returns through your browser exactly as before:
https://printer.example/oauth/callback
?code=demo-code-7
&state=demo-attempt-7
&iss=https%3A%2F%2Fauth.photos.example
The printer checks state and iss against its pending transaction and exchanges the code with its verifier and client authentication. Because the pushed request contained a redirect_uri, the token request repeats it, just as it would have if the address had travelled in the browser. Nothing in the code exchange changes.
Single use and a short life
The request URI passes through your browser, so it is exposed in the same ways any URL is. It can end up in history or a log. Someone who copies it could try to start the same authorization in a browser of their own. Several properties keep that from being useful.
Single use. Once the reference has been used, a copy is worth nothing. The specification says servers should treat request URIs as one-time use, and allows them to tolerate a repeated visit when someone reloads the page. Some servers apply the single use when the authorization is completed rather than when the page first loads, so that a link preview or a browser that fetches pages in advance does not use up the reference before you arrive.
A short lifetime. An unused reference stops working after its expires_in. The lifetime has to be long enough for a slow network or a phone that asks which browser to open, and short enough that a copy found later has already expired.
Bound to one client. Suppose an attacker registered their own client with the photo service, pushed a request naming their own return address, and then sent you a link carrying that reference with client_id=photo-printer. The consent screen would show the printer's name while the stored request sent the result to the attacker. The binding check in step 3 refuses that combination.
Unguessable. The random part of the reference means nobody can produce a valid one for a request they did not push.
The printer has a matching duty: it uses each reference once. If you close the photo service tab and select Connect again, the printer pushes a new request rather than reusing the old reference. The stored request contains values that belong to one attempt, such as state and the PKCE challenge, and a new attempt needs new ones.
What the browser can still affect
PAR removed the parameters from the browser, but the browser still carries two things: the reference on the way in and the response on the way out.
An attacker who captured a request URI from someone else's attempt might try swapping it into yours, hoping your approval completes a request you never saw. That stored request carries the state and PKCE challenge of the attempt that created it. When the response comes back to your session, the state does not match your pending transaction, and the code cannot be redeemed without the other attempt's verifier. This is one reason the pushed request should always include state and a PKCE challenge, even though nobody can edit them in transit any more.
The response is unchanged by PAR. The code, state, and iss come back in an ordinary redirect, readable and recordable like any URL, and the printer applies every check from Correlating requests and responses before it uses them. Protecting the response itself takes a different mechanism, which the JWT Secured Authorization Response Mode (JARM) group describes, starting with Protecting an authorization response.