Skip to content

Pushed Authorization Requests

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.

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
// Check if PAR endpoint is available
const available = await client.par.isAvailable();
// Check if the server requires PAR
const required = await client.par.isRequired();

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
PropertyTypeDescription
requestUristringOpaque reference to the stored authorization request
expiresAtnumberWhen the request URI expires (epoch seconds)

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.

// 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);

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
maxAgenumberMaximum authentication age in seconds
acrValuesstringAuthentication context
resourcestring | string[]Resource indicator
audiencestringTarget audience
extraParamsRecord<string, string>Additional parameters
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;
}
}