PAR and authorization details
Two extensions that matter most for agent authorization: PAR moves the authorization request off the browser URL, and Rich Authorization Requests replace a flat scope string with structured permissions.
Pushed authorization requests
Section titled “Pushed authorization requests”RFC 9126. Instead of putting every parameter in a front-channel redirect — where it is visible in
browser history, Referer headers, and server logs, and where anything not signed can be tampered
with — the client POSTs the request over the back channel and receives a handle.
POST /parAuthorization: Basic <base64(client_id:client_secret)>Content-Type: application/x-www-form-urlencoded
response_type=code&client_id=client_7f3a…&redirect_uri=https://app.acme.com/callback&scope=openid%20profile&code_challenge=<S256>&code_challenge_method=S256&state=…{ "request_uri": "urn:ietf:params:oauth:request_uri:8f3b2c…", "expires_in": 90}The front-channel redirect then carries only two parameters:
GET /authorize?client_id=client_7f3a…&request_uri=urn:ietf:params:oauth:request_uri:8f3b2c…The request_uri is single-use and valid for 90 seconds. Redeeming it consumes it, so a
replayed authorization request fails.
/par accepts response_type, redirect_uri, scope, state, nonce, code_challenge,
code_challenge_method, organization, resource (repeatable), and authorization_details.
What it validates up front
Section titled “What it validates up front”Because it runs on the back channel, /par can fail fast with a real error instead of a redirect:
- Confidential clients must authenticate, exactly as at
/token. redirect_urimust already be registered for the client.response_typemust becode.code_challengeis required, and onlyS256is accepted.- A pushed request may not itself carry a
request_uri.
That is the practical benefit during integration: a misconfigured redirect URI surfaces as a JSON error on your own server rather than as a redirect loop in someone’s browser.
PAR is supported but not required — discovery reports
require_pushed_authorization_requests: false.
Rich authorization details
Section titled “Rich authorization details”RFC 9396. A scope string says what kind of access; authorization_details says which resources,
which actions, and how much. For agents this is the difference between “may call tools” and “may
call these two tools, at this server, at most fifty times”.
[ { "type": "mcp_tool", "locations": ["https://tools.acme.com/mcp"], "tools": ["search", "weather"], "max_calls": 50 }]Send it URL-encoded on /authorize, or as a parameter on /par:
GET /authorize ?response_type=code &client_id=agent-research-bot &code_challenge=<S256> &authorization_details=%5B%7B%22type%22%3A%22mcp_tool%22%2C…%5Dmcp_tool is the only supported type today; discovery lists it under
authorization_details_types_supported. An unknown type, or malformed JSON, is rejected with
invalid_authorization_details rather than being silently dropped — a permission request that is
not understood must not be treated as no request at all.
Fields
Section titled “Fields”| Field | Meaning |
|---|---|
type | The detail type. Must be mcp_tool. |
locations | The resource(s) this detail applies to. |
actions | Actions being requested. |
tools | The tool names this grant authorizes. |
max_calls | An advisory per-grant call budget. |
In the token
Section titled “In the token”Granted details are echoed in the token response and carried as a claim in the access token:
{ "sub": "usr_01J…", "client_id": "agent-research-bot", "scope": "mcp:tools.invoke", "authorization_details": [ { "type": "mcp_tool", "locations": ["https://tools.acme.com/mcp"], "tools": ["search"] } ]}A resource server should enforce them alongside scope and audience. max_calls is advisory — the
authorization server records what was granted; counting invocations is the resource server’s job.
Using them together
Section titled “Using them together”For an agent connecting to an MCP server, the strong combination is: push the request with PAR so
the requested tools cannot be tampered with in the browser, describe the grant with
authorization_details so consent shows the user exactly which tools are being requested, bind the
audience with a resource indicator, and take the token
under DPoP so it cannot be used anywhere else.
See MCP authorization for that flow end to end.