Skip to content

Rich authorization requests

Security: rich authorization requests (RFC 9396)

A client that holds a person's access token can exchange it (RFC 8693) for a token that names exactly one action in authorization_details: one deployment plan, one payment. The resource server checks that the token approves the action it is asked to do, so a token minted for one plan cannot approve another.

POST /oauth/token
grant_type=urn:ietf:params:oauth:grant-type:token-exchange
&subject_token=<the person's access token>
&subject_token_type=urn:ietf:params:oauth:token-type:access_token
&resource=https://engine.example.com
&authorization_details=[{"type":"cbox_infra_change","plan_digest":"sha256:…"}]

The response echoes authorization_details (§7), and the access token carries them as a claim (§9.1).

Controls

Control Mechanism Where
Registered types only every entry's type must be in the client's authorization_details_types (§10); null = none, so no existing client changes Client::allowsAuthorizationDetailsType(), TokenExchangeService
Bounded shape a JSON array of 1–10 objects, each with a string type; values are strings, numbers, booleans, arrays and objects only, at most four deep, 4 KB in all AuthorizationDetails::fromRequest()
Same person, same client the exchange's usual rules hold: an active subject token issued to (or naming) the caller, down-scope only, DPoP continuity, never an acted token TokenExchangeService
No newer login than the real one the new token carries the subject token's own auth_time and acr (RFC 9470), never fresh ones, so a resource server can still demand a recent or two-factor login JwtTokenIssuer::issueForExchange()
Not forgeable by a hook authorization_details is a reserved claim; a TokenMinting hook cannot add, change or strip it JwtTokenIssuer::RESERVED_CLAIMS
Refused loudly anything malformed or unregistered is 400 invalid_authorization_details (§5) TokenController

What it does not prove

The exchange proves that the client held a live token of this person and asked for this action. It does not prove that the person saw the action. When that matters (an agent acting for someone), use CIBA, where the person approves the binding message on their own device. When the person is in the client's own browser, the client obtains a fresh token for them with an authorization-code redirect, with max_age and acr_values for step-up (RFC 9470), and exchanges that token immediately.

Registering a client

An operator sets the types on the client:

$client->forceFill(['authorization_details_types' => ['cbox_infra_change']])->save();