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.
Overview
Section titled “Overview”- RFC: RFC 9126 - OAuth 2.0 Pushed Authorization Requests
- Status: Fully Implemented
- Endpoint:
POST /par
Why Use PAR?
Section titled “Why Use PAR?”Security Benefits
Section titled “Security Benefits”-
Parameter Tampering Prevention
- Authorization parameters are sent via secure backend channel
- Parameters cannot be modified by user or intermediaries
- Request integrity is guaranteed
-
Privacy Protection
- Sensitive parameters not exposed in browser history
- No leakage through referrer headers
- Prevents surveillance of authorization requests
-
URL Length Limitations
- Avoids browser/server URL length limits
- Enables complex requests with many parameters
- Supports large request objects (JAR)
-
Client Authentication
- PAR endpoint can require client authentication
- Prevents unauthorized authorization requests
- Reduces phishing risks
Use Cases
Section titled “Use Cases”- Financial Services (FAPI compliance)
- Healthcare (HIPAA-compliant OAuth flows)
- Enterprise (complex authorization with many parameters)
- Mobile Apps (secure authorization from native apps)
How PAR Works
Section titled “How PAR Works”Flow Overview
Section titled “Flow Overview”- Client pushes parameters: Client sends authorization parameters to PAR endpoint
- Server stores parameters: Server validates and stores parameters (10-minute TTL)
- Server returns request_uri: Server responds with a unique
request_uri - Client redirects user: Client redirects user to authorization endpoint with
request_uri - Server retrieves parameters: Authorization endpoint retrieves stored parameters
- Normal flow continues: Authorization proceeds as normal
API Reference
Section titled “API Reference”PAR Endpoint
Section titled “PAR Endpoint”POST /par
Request Format
Section titled “Request Format”POST /par HTTP/1.1Content-Type: application/x-www-form-urlencodedAuthorization: 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=S256Request Parameters
Section titled “Request Parameters”| Parameter | Required | Description |
|---|---|---|
client_id | Yes | Client identifier |
response_type | Yes | OAuth response type (e.g., code) |
redirect_uri | Yes | Client’s registered redirect URI |
scope | Yes | Requested scopes (space-separated) |
state | No | Opaque value for CSRF protection |
nonce | No | Nonce for ID token binding (OIDC) |
code_challenge | No | PKCE code challenge |
code_challenge_method | No | PKCE method (must be S256) |
Success Response
Section titled “Success Response”Status: 201 Created
{ "request_uri": "urn:ietf:params:oauth:request_uri:6esc_11ACC5bwc014ltc14eY22c", "expires_in": 600}| Field | Type | Description |
|---|---|---|
request_uri | string | Unique URN identifying the stored request |
expires_in | number | Lifetime in seconds (default: 600 = 10 minutes) |
Authorization Endpoint with PAR
Section titled “Authorization Endpoint with PAR”GET /authorize
GET /authorize ?request_uri=urn:ietf:params:oauth:request_uri:6esc_11ACC5bwc014ltc14eY22c &client_id=my_client_idHost: auth.example.com| Parameter | Required | Description |
|---|---|---|
request_uri | Yes | The request URI from PAR response |
client_id | Yes | Must match client_id from PAR request |
Usage Examples
Section titled “Usage Examples”Basic PAR Flow
Section titled “Basic PAR Flow”Step 1: Push Authorization Request
Section titled “Step 1: Push Authorization Request”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();PAR with PKCE
Section titled “PAR with PKCE”# Generate PKCE challengeCODE_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 PKCEcurl -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"JavaScript Client Library
Section titled “JavaScript Client Library”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();}Implementation Details
Section titled “Implementation Details”Request URI Format
Section titled “Request URI Format”- Scheme:
urn:ietf:params:oauth:request_uri: - Identifier: Cryptographically secure random string
- Example:
urn:ietf:params:oauth:request_uri:6esc_11ACC5bwc014ltc14eY22c
Security Features
Section titled “Security Features”- Single-Use: Request URIs are deleted immediately after retrieval
- Short Lifetime: Default 10 minutes (600 seconds)
- Client ID Matching:
client_idin authorization request must match PAR request - Secure Storage: Parameters stored in KV with automatic expiration
Comparison: Traditional vs PAR
Section titled “Comparison: Traditional vs PAR”Traditional Authorization Request
Section titled “Traditional Authorization Request”GET /authorize ?response_type=code &client_id=my_client_id &redirect_uri=https://myapp.example.com/callback &scope=openid+profile+email &state=abc123Issues:
- Parameters exposed in URL
- Visible in browser history
- Limited by URL length
- Can be tampered with
PAR Authorization Request
Section titled “PAR Authorization Request”POST /par → {request_uri}
GET /authorize?request_uri=urn:ietf:...&client_id=my_client_idBenefits:
- Parameters sent securely via POST
- Not visible in browser history
- No URL length limitations
- Cannot be tampered with
Error Responses
Section titled “Error Responses”{ "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"}Compliance
Section titled “Compliance”- 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
Discovery Metadata
Section titled “Discovery Metadata”PAR support is advertised in the OpenID Provider metadata:
{ "pushed_authorization_request_endpoint": "https://auth.example.com/par", "require_pushed_authorization_requests": false}