Skip to content

Pushed Authorization Requests (PAR)

Pushed Authorization Requests (PAR) is an OAuth 2.0 extension defined in RFC 9126 that moves authorization parameters from the browser URL to a server-to-server POST request. This prevents parameter tampering, reduces URL length, and hides sensitive data (scopes, redirect URIs, PKCE challenges) from the browser address bar.

In a standard authorization code flow, all parameters are visible in the browser URL:

/connect/authorize?client_id=my-app&response_type=code&scope=openid+profile+email
&redirect_uri=https://app.example.com/callback&state=abc123
&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
&code_challenge_method=S256

An attacker with browser access can:

  • Inspect parameters (leaking scopes, redirect URIs, client IDs)
  • Tamper with parameters (modifying scopes, injecting malicious redirect URIs)
  • Trigger URL truncation (complex requests exceed URL length limits)

PAR eliminates all three risks by pushing parameters server-side first.

sequenceDiagram
    participant BFF as BFF Server
    participant IdP as OpenIddict Server
    participant Browser

    BFF->>IdP: POST /connect/par<br/>(client_id, client_secret,<br/>response_type, redirect_uri,<br/>scope, state, code_challenge)
    IdP-->>BFF: 201 Created<br/>{ request_uri, expires_in }

    BFF->>Browser: 302 Redirect<br/>/connect/authorize?client_id=...&request_uri=...
    Browser->>IdP: GET /connect/authorize?client_id=...&request_uri=...
    Note over IdP: Resolves request_uri<br/>to original parameters
    IdP-->>Browser: Login page → Authorization code
  1. Push parameters — The BFF (or any confidential client) POSTs all authorization parameters to /connect/par with client authentication.

  2. Receive request_uri — The server validates the request, stores the parameters, and returns an opaque request_uri (valid for a short time).

  3. Redirect with request_uri — The browser is redirected to /connect/authorize with only client_id and request_uri — no sensitive parameters in the URL.

  4. Normal flow continues — The authorization server resolves the request_uri, authenticates the user, and issues an authorization code as usual.

The PAR endpoint (/connect/par) is always available — registered automatically by AddGranitOpenIddict(). No additional server configuration is needed for optional PAR.

To require PAR for all authorization code flows (reject direct requests without a request_uri):

{
"OpenIddict": {
"RequirePar": true
}
}

Enable PAR on a frontend to automatically push parameters before redirecting:

{
"Bff": {
"Frontends": [
{
"Name": "admin",
"UsePushedAuthorizationRequests": true
}
]
}
}

When enabled, the BFF login flow:

  1. POSTs all authorization parameters to /connect/par
  2. Receives request_uri from the server
  3. Redirects the browser with only client_id + request_uri
  4. Falls back to direct parameters if PAR fails (graceful degradation)

A successful PAR request returns 201 Created with a JSON body:

{
"request_uri": "urn:openiddict:params:request:abc123def456",
"expires_in": 60
}
FieldTypeDescription
request_uristringOpaque reference to the stored authorization parameters
expires_inintSeconds until the request_uri expires (typically 60s)
StatusErrorCause
400 Bad Requestinvalid_requestMissing or malformed parameters
400 Bad Requestinvalid_clientMissing or invalid client authentication
400 Bad Requestinvalid_redirect_uriRedirect URI not registered for this client
403 Forbiddeninsufficient_scopeClient lacks ept:pushed_authorization permission
Browser URL (visible, tamperable):
/connect/authorize
?client_id=my-app
&response_type=code
&scope=openid profile email offline_access
&redirect_uri=https://app.example.com/callback
&state=xyzSecretState123
&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
&code_challenge_method=S256

All parameters visible in browser history, logs, and referrer headers.

ThreatMitigation
Parameter tampering in URLParameters are server-side, not in URL
PII leakage via browser historyOnly opaque request_uri in URL
URL truncation (long scopes)Parameters sent via POST body
Replay of request_uriShort-lived (typically 60 seconds), single-use
Unauthorized PAR requestsClient authentication required on /connect/par
Man-in-the-browserCombined with PKCE, provides defense in depth

PAR is mandatory in the FAPI 2.0 Security Profile alongside DPoP and PKCE. Industries requiring FAPI 2.0 include:

  • Open Banking (PSD2, UK Open Banking)
  • Healthcare (HL7 SMART on FHIR)
  • Government (eIDAS, Digital Identity)
  • Insurance (ACORD standards)