ASVS 5.0 V10.4.13 (Level 3) requires that the authorization code grant is always used together with pushed authorization requests (PAR). In practice, the authorization endpoint should refuse any request that does not reference a request URI obtained from the PAR endpoint.
Why it matters
Normally the client puts parameters such as scope, redirect URI and challenge in the browser URL. The user, or malware in the browser, can alter them, and long URLs leak into logs. With PAR the client first posts the parameters to the server over an authenticated back channel and receives a short-lived request_uri. The browser only carries that reference.
How to implement it
The client side call looks like this:
curl -s https://auth.example.com/par \
-u "orders-backend:$CLIENT_SECRET" \
-d response_type=code \
-d client_id=orders-backend \
-d redirect_uri=https://orders.example.com/cb \
-d scope=orders.read \
-d code_challenge="$CHALLENGE" \
-d code_challenge_method=S256
The response contains a request_uri and an expires_in. The client then sends the user to the authorization endpoint with client_id and request_uri only.
On the server, set require_pushed_authorization_requests for the client, or globally at L3. Keep the request URI single use and short-lived, and bind it to the client that pushed it.
How to test it
Send a plain authorization request that carries parameters directly in the URL. Expect rejection. Send one with a valid request_uri. Expect success. Replay the same request_uri and expect failure. Present it with a different client_id and expect failure.
Interaction with other requirements
PAR works best together with its neighbors in this chapter. Use PKCE (V10.4.6) inside the pushed request, authenticate the client on the PAR endpoint (V10.4.10), and keep codes short-lived and single use (V10.4.2 and V10.4.3). Each closes a gap the others leave open.
Common mistakes
- Offering PAR as optional.
- Leaving the PAR endpoint unauthenticated for confidential clients (see V10.4.10).
- Reusable request URIs.
Quick check
What does the browser carry when PAR is used?
Only the client ID and a short-lived request URI, not the authorization parameters.