Skip to content

PAR

PAR (RFC 9126) is an OAuth 2.0 security extension that allows clients to push authorization request parameters directly to the authorization server before redirecting the user, providing enhanced security and privacy.

  1. Parameter Tampering Prevention

    • Authorization parameters are sent via secure backend channel
    • Parameters cannot be modified by user or intermediaries
    • Request integrity is guaranteed
  2. Privacy Protection

    • Sensitive parameters not exposed in browser history
    • No leakage through referrer headers
    • Prevents surveillance of authorization requests
  3. URL Length Limitations

    • Avoids browser/server URL length limits
    • Enables complex requests with many parameters
    • Supports large request objects (JAR)
  4. Client Authentication

    • PAR endpoint can require client authentication
    • Prevents unauthorized authorization requests
    • Reduces phishing risks
  • Financial Services (FAPI compliance)
  • Healthcare (HIPAA-compliant OAuth flows)
  • Enterprise (complex authorization with many parameters)
  • Mobile Apps (secure authorization from native apps)
  1. Client pushes parameters: Client sends authorization parameters to PAR endpoint
  2. Server stores parameters: Server validates and stores parameters (10-minute TTL)
  3. Server returns request_uri: Server responds with a unique request_uri
  4. Client redirects user: Client redirects user to authorization endpoint with request_uri
  5. Server retrieves parameters: Authorization endpoint retrieves stored parameters
  6. Normal flow continues: Authorization proceeds as normal

POST /par

POST /par HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Authorization: Basic <base64(client_id:client_secret)>
client_id=my_client_id
&response_type=code
&redirect_uri=https://myapp.example.com/callback
&scope=openid+profile+email
&state=abc123
&nonce=xyz789
&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
&code_challenge_method=S256
ParameterRequiredDescription
client_idYesClient identifier
response_typeYesOAuth response type (e.g., code)
redirect_uriYesClient’s registered redirect URI
scopeYesRequested scopes (space-separated)
stateNoOpaque value for CSRF protection
nonceNoNonce for ID token binding (OIDC)
code_challengeNoPKCE code challenge
code_challenge_methodNoPKCE method (must be S256)

Status: 201 Created

{
"request_uri": "urn:ietf:params:oauth:request_uri:6esc_11ACC5bwc014ltc14eY22c",
"expires_in": 600
}
FieldTypeDescription
request_uristringUnique URN identifying the stored request
expires_innumberLifetime in seconds (default: 600 = 10 minutes)

GET /authorize

GET /authorize
?request_uri=urn:ietf:params:oauth:request_uri:6esc_11ACC5bwc014ltc14eY22c
&client_id=my_client_id
Host: auth.example.com
ParameterRequiredDescription
request_uriYesThe request URI from PAR response
client_idYesMust match client_id from PAR request
Terminal window
curl -X POST https://auth.example.com/par \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "client_id=my_client_id" \
-d "response_type=code" \
-d "redirect_uri=https://myapp.example.com/callback" \
-d "scope=openid profile email" \
-d "state=abc123" \
-d "nonce=xyz789"

Response:

{
"request_uri": "urn:ietf:params:oauth:request_uri:6esc_11ACC5bwc014ltc14eY22c",
"expires_in": 600
}

Step 2: Redirect User to Authorization Endpoint

Section titled “Step 2: Redirect User to Authorization Endpoint”
const authUrl = new URL('https://auth.example.com/authorize');
authUrl.searchParams.set('request_uri', 'urn:ietf:params:oauth:request_uri:6esc_11ACC5bwc014ltc14eY22c');
authUrl.searchParams.set('client_id', 'my_client_id');
window.location.href = authUrl.toString();
Terminal window
# Generate PKCE challenge
CODE_VERIFIER=$(openssl rand -base64 96 | tr -d '\n' | tr '/+' '_-' | tr -d '=')
CODE_CHALLENGE=$(echo -n $CODE_VERIFIER | openssl dgst -binary -sha256 | openssl base64 | tr -d '\n' | tr '/+' '_-' | tr -d '=')
# Push authorization request with PKCE
curl -X POST https://auth.example.com/par \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "client_id=my_public_client" \
-d "response_type=code" \
-d "redirect_uri=https://myapp.example.com/callback" \
-d "scope=openid profile" \
-d "code_challenge=$CODE_CHALLENGE" \
-d "code_challenge_method=S256"
async function authorizeWithPAR(config: {
clientId: string;
redirectUri: string;
scope: string;
state: string;
nonce: string;
}) {
// Step 1: Push authorization request
const parResponse = await fetch('https://auth.example.com/par', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams({
client_id: config.clientId,
response_type: 'code',
redirect_uri: config.redirectUri,
scope: config.scope,
state: config.state,
nonce: config.nonce,
}),
});
const { request_uri } = await parResponse.json();
// Step 2: Redirect to authorization endpoint
const authUrl = new URL('https://auth.example.com/authorize');
authUrl.searchParams.set('request_uri', request_uri);
authUrl.searchParams.set('client_id', config.clientId);
window.location.href = authUrl.toString();
}
  • Scheme: urn:ietf:params:oauth:request_uri:
  • Identifier: Cryptographically secure random string
  • Example: urn:ietf:params:oauth:request_uri:6esc_11ACC5bwc014ltc14eY22c
  1. Single-Use: Request URIs are deleted immediately after retrieval
  2. Short Lifetime: Default 10 minutes (600 seconds)
  3. Client ID Matching: client_id in authorization request must match PAR request
  4. Secure Storage: Parameters stored in KV with automatic expiration
GET /authorize
?response_type=code
&client_id=my_client_id
&redirect_uri=https://myapp.example.com/callback
&scope=openid+profile+email
&state=abc123

Issues:

  • Parameters exposed in URL
  • Visible in browser history
  • Limited by URL length
  • Can be tampered with
POST /par → {request_uri}
GET /authorize?request_uri=urn:ietf:...&client_id=my_client_id

Benefits:

  • Parameters sent securely via POST
  • Not visible in browser history
  • No URL length limitations
  • Cannot be tampered with
{
"error": "invalid_request",
"error_description": "client_id is required"
}
{
"error": "invalid_request",
"error_description": "Invalid or expired request_uri"
}
{
"error": "invalid_request",
"error_description": "client_id mismatch"
}
  • FAPI 2.0: PAR is mandatory for FAPI 2.0 compliance
  • OAuth 2.1-style deployments: PAR is recommended for stronger request integrity
  • OpenID Connect: Compatible with all OIDC flows

PAR support is advertised in the OpenID Provider metadata:

{
"pushed_authorization_request_endpoint": "https://auth.example.com/par",
"require_pushed_authorization_requests": false
}