PE
Protocol Explorer
OAuth 2.0

OAuth 2.0 — Pushed Authorization Requests (PAR)

Pushed Authorization Requests (RFC 9126) flip the normal authorization flow: instead of encoding the full authorization request in the browser redirect URL, the client POSTs the request directly to the AS server-to-server first and receives a short-lived request_uri. The browser redirect carries only the client_id and request_uri. This prevents tampering (request params never touch the browser), supports arbitrarily large requests (e.g., Rich Authorization Requests), and gives the AS early validation before the user ever sees a prompt.

RFC 9126
Web AppAuthorization ServerBrowser / UserResource Server1POST /par (push auth reques…2012GET /authorize?request_uri=…3POST /login (credentials + …4GET /callback?code=...&stat…5POST /token (code exchange …6GET /api/profile
POST https://auth.example.com/par201

The client POSTs the full authorization request to the AS's PAR endpoint server-to-server, before involving the browser. The AS validates the request immediately and returns a short-lived request_uri that acts as a pointer to the stored request. The client authenticates in the same way it would at the token endpoint.

RFC 9126 §2.1: The PAR endpoint MUST require client authentication for confidential clients.

RFC 9126 §2.2: The AS responds with 201 Created (not 200) when the request is successfully stored.

expires_in=60: The request_uri must be used within 60 seconds or it expires. This prevents stale authorization requests.

Scopes (`read:profile`, `write:reports`) are application-defined permission strings that describe exactly what access is being requested on the user's behalf. The AS validates that this client is permitted to request these scopes before issuing the request_uri.

PAR pairs naturally with JAR (JWT-Secured Authorization Requests, RFC 9101) — the pushed body can be a signed JWT for non-repudiation of the authorization request itself.

PAR is required by FAPI 2.0 Security Profile for high-security financial-grade APIs.

1 / 6
speed

Step 1: POST /par (push auth request)

Request / response
POSThttps://auth.example.com/par
Content-Type?

application/x-www-form-urlencoded

AuthorizationOAuth?

Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW

Body
response_type=code&client_id=s6BhdRkqt3&redirect_uri=https%3A%2F%2Fapp.example.com%2Fcallback&scope=read:profile+write:reports&state=af0ifjsldkj&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM&code_challenge_method=S256
Cryptographic Signature

Construction Steps

1. 1. Why push the request first?
Traditional authorization code flow puts ALL parameters in the browser
redirect URL:

  https://as.example.com/authorize
    ?response_type=code
    &client_id=s6BhdRkqt3
    &redirect_uri=https://app.example.com/callback
    &scope=read:profile+write:reports
    &state=af0ifjsldkj
    &code_challenge=E9Melhoa...    ← visible in browser history
    &code_challenge_method=S256      and server logs

Problems with URL-based requests:
  • Visible in browser history, server logs, Referer headers
  • Can be tampered with before the AS receives them
  • URL length limits prevent large payloads (e.g., RAR authorization_details)
  • No AS validation until the user reaches the AS

PAR solution: POST the full request directly to the AS first.
2. 2. What the AS validates immediately (pre-flight)
On receiving the PAR request, the AS validates everything NOW—before
the user ever sees a browser prompt:

  • Client authentication (Basic auth header)
  • client_id is registered
  • redirect_uri exactly matches a registered URI
  • response_type and requested scopes (read:profile, write:reports) are permitted for this client
  • code_challenge is present (if PKCE is required)

If any check fails → 400 error response to the client.
The browser is never involved in a failed request — no confusing
error pages for the user, and no partial state on the AS.
3. 3. The request_uri — a short-lived pointer
request_uri = "urn:ietf:params:oauth:request_uri:6esc_11ACC5bwc014ltc14eY"

RFC 9126 §2.2: The request_uri:
  • Format is at the AS's discretion (urn:ietf:params:oauth:request_uri: is conventional)
  • Is a single-use opaque reference to the stored request object
  • Expires in `expires_in` seconds (typically 30–60 seconds)
  • Is bound to the client_id that created it

The stored request object includes ALL original parameters.
The browser URL in the next step carries ONLY client_id + request_uri.

Signature Base String

Full authorization request parameters posted server-to-server

Signing Key

Client authenticates with client_secret_basic (same as token endpoint)

Signature Output

request_uri = urn:ietf:params:oauth:request_uri:6esc_11ACC5bwc014ltc14eY (expires in 60s)