PE
Protocol Explorer
OAuth 2.0

OAuth 2.0 — Rich Authorization Requests (RAR)

Rich Authorization Requests (RFC 9396) replace coarse-grained scopes with a structured authorization_details parameter that precisely describes what the client is authorized to do. Instead of scope=payments (which could mean 'make any payment'), the client specifies the exact payment type, amount, currency, and recipient. The AS embeds the granted authorization_details in the access token, and the resource server enforces the specific constraints — not just 'can make payments' but 'can make exactly this payment'.

RFC 9396
Browser / UserBanking App (RP)Authorization ServerPayments API1Initiate payment of €2503022GET /authorize (with author…3POST /login (authenticate +…4GET /callback?code=...&stat…5POST /token (code exchange)6POST /payments (initiate pa…
POST https://bankapp.example.com/payments/initiate302

The user initiates a €250 payment. The client builds a structured authorization_details JSON describing the exact payment operation and redirects the browser to the AS with it encoded in the authorization request. This is the key difference from scope-based authorization — the intent is fully specified upfront.

RFC 9396 §2: authorization_details is a JSON array encoded as a query parameter (URL-encoded JSON string).

For large authorization_details payloads, PAR (RFC 9126) should be used to push the request server-to-server rather than embedding it in the URL. PAR + RAR is the recommended combination for financial-grade APIs.

The authorization_details value is what the AS will show in the consent UI — specific enough for the user to make an informed decision.

RFC 9396 §3: If authorization_details and scope are both present, both are applied. They are complementary, not mutually exclusive.

1 / 6
speed

Step 1: Initiate payment of €250

Request / response
POSThttps://bankapp.example.com/payments/initiate
Content-Type?

application/json

Cookie

session=user_session_abc

Body
{
  "amount": "250.00",
  "currency": "EUR",
  "recipient": "John Smith",
  "iban": "DE02100100109307118603",
  "reference": "Invoice #2024-0042"
}
Cryptographic Signature

Construction Steps

1. 1. Why scopes are not enough
Traditional scope-based authorization for payments:

  scope = "payments:write"

This tells the AS: "the client wants to make payments."
It tells the RS nothing useful:
  • How much? (could be any amount)
  • To whom? (could be any recipient)
  • Which account? (not specified)
  • One payment or unlimited? (not specified)

The RS must trust the client to stay within bounds — or
implement its own out-of-band authorization system.
Scope is a permission category, not a permission contract.
2. 2. authorization_details — the structured alternative
authorization_details = [
  {
    "type": "payment_initiation",
    "actions": ["initiate"],
    "locations": ["https://payments.example.com"],
    "instructedAmount": {
      "currency": "EUR",
      "amount": "250.00"
    },
    "creditorName": "John Smith",
    "creditorAccount": {
      "iban": "DE02100100109307118603"
    },
    "remittanceInformation": "Invoice #2024-0042"
  }
]

This authorization is NOT for 'make payments'.
It is ONLY for: initiate exactly one €250 payment
to DE02100100109307118603 (John Smith).

RFC 9396 §2: authorization_details is a JSON array.
Each element MUST have a type field; other fields are
type-specific and defined by the authorization type's
own specification (e.g., Open Banking, FHIR, etc.).
3. 3. Type registry and extensibility
The "type" field is the key to RAR extensibility:

  "payment_initiation"  → Open Banking / PSD2 payments
  "openid_credential"   → OpenID for Verifiable Credentials
  "account_information" → Open Banking account read
  "medical_record"      → FHIR-based health data access
  "document_access"     → specific document + permissions

Each type defines its own schema. The AS understands
the type to show an appropriate consent UI.
The RS understands the type to enforce the exact
operation that was authorized.

authorization_details can coexist with scope in the same
request — they are additive, not mutually exclusive.

Signature Base String

{ amount: 250.00 EUR, recipient: John Smith, iban: DE02100100109307118603 }

Signing Key

Client builds authorization_details JSON from the payment intent

Signature Output

authorization_details=[{"type":"payment_initiation", ...}] encoded in the authorization URL