Skip to content

Pushed Authorization Requests

Overview

Pushed Authorization Requests (PAR, RFC 9126) allow the client to send authorization parameters to the authorization server via a back-channel POST request, instead of including them in the authorization URL query string. This provides:

  • Parameter confidentiality — Authorization parameters are not exposed in the browser address bar or HTTP referer headers
  • Request integrity — Parameters cannot be tampered with during the redirect
  • Large parameter support — No URL length limitations
  • Required by FAPI 2.0 — Mandatory for financial-grade API compliance

The PAR API is available at client.par.

How PAR Works

sequenceDiagram
    participant App
    participant SDK
    participant AS as Auth Server

    App->>SDK: Login
    SDK->>AS: POST /par (all params)
    AS->>SDK: request_uri
    SDK-->>AS: Redirect with request_uri only
  1. The SDK sends all authorization parameters to the PAR endpoint via POST
  2. The server returns a request_uri (an opaque reference to the stored parameters)
  3. The authorization URL contains only the request_uri and client_id — no sensitive parameters

Usage

Checking Availability

// Check if PAR endpoint is available
const available = await client.par.isAvailable();
// Check if the server requires PAR
const required = await client.par.isRequired();

Pushing Authorization Parameters

Send authorization parameters to the PAR endpoint:

const parResult = await client.par.push({
redirectUri: 'https://myapp.com/callback',
scope: 'openid profile email',
});
console.log(parResult.requestUri); // urn:ietf:params:oauth:request_uri:...
console.log(parResult.expiresAt); // Epoch seconds

PARResult

PropertyTypeDescription
requestUristringOpaque reference to the stored authorization request
expiresAtnumberWhen the request URI expires (epoch seconds)

Building the Authorization URL

After pushing, build the authorization URL with just the request_uri:

const url = await client.par.buildAuthorizationUrl(parResult.requestUri);
// Redirect the user
window.location.href = url;

The generated URL contains only client_id and request_uri — all other parameters are stored server-side.

Complete Flow

// 1. Push parameters
const parResult = await client.par.push({
redirectUri: 'https://myapp.com/callback',
scope: 'openid profile email',
loginHint: '[email protected]',
});
// 2. Build URL with request_uri
const url = await client.par.buildAuthorizationUrl(parResult.requestUri);
// 3. Redirect
window.location.href = url;
// 4. Handle callback (same as standard Authorization Code Flow)
const tokens = await client.handleCallback(window.location.href);

Using PAR with buildAuthorizationUrl

You can also use PAR transparently through buildAuthorizationUrl() by setting usePar: true:

const { url } = await client.buildAuthorizationUrl({
redirectUri: 'https://myapp.com/callback',
scope: 'openid profile email',
usePar: true,
});
// The SDK automatically pushes parameters via PAR
// and builds the URL with the request_uri
window.location.href = url;

API Reference

Push Options

The push() method accepts the same options as buildAuthorizationUrl():

ParameterTypeDefaultDescription
redirectUristringRequiredRedirect URI
scopestring'openid profile'Requested scopes
responseType'code' | 'none''code'Response type
promptstringPrompt behavior
loginHintstringLogin hint
acrValuesstringAuthentication context
extraParamsRecord<string, string>Additional parameters

Error Handling

Error CodeDescriptionRecovery
par_errorPAR request failedCheck parameters, retry
par_requiredServer requires PAR but it wasn’t usedUse usePar: true or call par.push()
no_par_endpointPAR endpoint not found in discoveryServer doesn’t support PAR
try {
const parResult = await client.par.push({
redirectUri: 'https://myapp.com/callback',
});
} catch (error) {
if (error.code === 'no_par_endpoint') {
// Fall back to standard authorization URL
const { url } = await client.buildAuthorizationUrl({
redirectUri: 'https://myapp.com/callback',
});
window.location.href = url;
}
}

References

Next Steps