Pushed Authorization Requests (PAR)
Pushed Authorization Requests (RFC 9126)
let a client send an authorization request's parameters directly to O2ID
over a back-channel POST, instead of putting all of them on the
browser-visible /oauth2/authorize URL. This is optional — see the
Authorization Code Grant for
the parameters a plain /oauth2/authorize request takes on its own.
Initializing a Pushed Authorization Request
POST /t/{tenant}/oauth2/par — form-encoded, same parameter names
/oauth2/authorize accepts:
curl -X POST http://localhost:8080/t/system/oauth2/par \
-d response_type=code \
-d client_id="$CLIENT_ID" \
-d redirect_uri=http://localhost:3000/callback \
-d scope="openid profile" \
-d code_challenge="$CODE_CHALLENGE" \
-d code_challenge_method=S256
$CODE_CHALLENGE is the PKCE challenge — see PKCE
for how to generate one; PKCE is required here exactly as it is for an
ordinary authorization request.
The response is:
{
"request_uri": "urn:ietf:params:oauth:request_uri:...",
"expires_in": 90
}
Initializing Authorize Request
Pass that request_uri to /oauth2/authorize instead of the individual
parameters — only client_id still needs to be present there, and it
must match the one the pushed request was created under:
GET /oauth2/authorize?client_id={clientId}&request_uri={request_uri}
From here the flow continues exactly as an ordinary authorization
request would — redirecting to login (if needed), then consent, then the
registered redirect_uri.
Expiration and Reuse
A request_uri expires after 90 seconds by default. It isn't deleted on
first use, only on expiry: if the user isn't authenticated yet, O2ID
redirects to login and the browser replays the same request_uri after
signing in, which still resolves to the same pushed parameters. The TTL is
the only thing that ever invalidates it.
The 90-second default is configurable per tenant (there is no
per-application override, since a request_uri isn't tied to an
application-scoped concept the way tokens or sessions are):
o2idctl tenant-settings update --par-request-uri-lifetime 120
Pass 0 or inherit to clear the override and fall back to the
built-in 90-second default. The same field is available as
pushedAuthorizationRequestLifetimeSeconds on GET/PATCH /settings.