# OAuth for agent delegation

Source: https://permdock.com/docs/standards/oauth-agent-delegation

How RFC 9396 Rich Authorization Requests, RFC 8693 token exchange, DPoP, CIBA and the IETF agent-delegation drafts shape PermDock's two-principal subject and the rule that an agent can never exceed its user.

Draft posture: build (RFC 8693, RFC 9396, RFC 9449; remaining IETF agent-delegation drafts tracked on the watch list)

PermDock implements the two-principal subject (`principal`, `actor`, `delegation`), matches RAR `authorization_details` against permissions, records RFC 8693 `act` chains, and derives `actor` from Web Bot Auth signatures.

## What it is [#what-it-is]

A cluster of OAuth specifications and drafts describes how a human's authority is handed to software that acts for them:

* **RFC 9396 Rich Authorization Requests (RAR).** Replaces flat scopes with an `authorization_details` array of typed JSON objects (`type`, `actions`, `locations`, `datatypes`, and type-specific fields). It is the carrier of constrained authority: not "may access posts" but "may update posts where the author is this user".
* **RFC 8693 token exchange.** Lets a client trade one token for another, optionally with an `actor_token`, producing tokens that name both who the request is for and who is making it (`act` claim).
* **DPoP (RFC 9449).** Binds a token to a key so a stolen token is useless without the private key; relevant when agents hold long-lived credentials.
* **CIBA.** Client-Initiated Backchannel Authentication: the agent asks, the user approves on a separate device. A standards-based human-in-the-loop primitive.
* **[Agent Delegation Chain](https://datatracker.ietf.org/doc/html/draft-asor-wimse-agent-delegation-chain-00)** (WIMSE draft): carries authority as RAR `authorization_details` across multiple agent hops and enforces **offline-verifiable monotonic attenuation**: each hop can only narrow what it received, and a verifier can check the whole chain without calling back to the issuer.
* **[Credential Delegation Protocol](https://datatracker.ietf.org/doc/draft-sweeney-wimse-credential-delegation/)** (WIMSE draft): composes RFC 8693, DPoP, RAR and CIBA into one delegation flow.
* **[OAuth for AI agents on behalf of a user](https://datatracker.ietf.org/doc/html/draft-oauth-ai-agents-on-behalf-of-user-02)**: adds `requested_actor` to the authorization request and `actor_token` to the token request so the resulting token names the agent as well as the user.
* **MCP Enterprise-Managed Authorization.** ID-JAG via RFC 8693 token exchange redeemed with RFC 7523; see [MCP authorization](/docs/standards/mcp-authorization).
* **Transaction tokens.** Short-lived tokens that carry authorization context across microservices inside a trust domain.

## Why it matters for PermDock [#why-it-matters-for-permdock]

The net effect of these specifications is that an authorization check is not "can this user" but "can this user, acting through this agent, under this delegated authority". A single-subject API cannot express that, so PermDock's subject has two principals:

* `principal`: the human or service whose grants are evaluated.
* `actor` (optional): the agent making the call: an MCP `clientId`, an AI SDK agent name, a Web Bot Auth key, an A2A card.
* `delegation` (optional): the authority the principal handed to the actor: OAuth scopes and/or RAR `authorization_details`, or an attenuated delegation chain.

A decision is **principal grants intersected with delegated authority**. The intersection is what makes "an agent can never exceed its user" a structural property rather than a policy the developer has to remember to write. See [subject](/docs/concepts/subject) and [delegation](/docs/security/delegation).

## How PermDock uses it [#how-permdock-uses-it]

```ts
import { createPermDock } from "permdock";

// Human case
const permdock = await createPermDock(policy, user);

// Agent case: actor + delegation
const asAgent = await createPermDock(policy, user, {
  actor: { id: authInfo.clientId, kind: "mcp-client" },
  delegation: {
    scopes: authInfo.scopes, // OAuth scopes
    authorizationDetails: authInfo.authorization_details, // RFC 9396 objects, if present
  },
});
asAgent.can(permissions.post.update, post); // true only if the user may AND the delegation covers it
```

Adapters fill `actor` and `delegation` automatically from `authInfo` (MCP), `runtimeContext` (AI SDK), the verified signature (Web Bot Auth) or the Agent Card (A2A), so application code rarely constructs them by hand.

### Matching `authorization_details` to permissions [#matching-authorization_details-to-permissions]

Each permission reference carries a `scope` (`post:update`), and RAR entries are matched on the permission's resource and action. An entry in `delegation.authorizationDetails` covers a permission when its `type` equals the resource name and its `actions`, when present, include the permission's action. An `identifier`, when present, narrows the entry to the one resource whose id it names; an empty `actions` array covers nothing. Other members (`locations`, `datatypes`, type-specific constraints) are the authorization server's to render and enforce; PermDock does not widen or narrow a grant from them, and the principal's own grant conditions still apply.

```json
{
  "type": "post",
  "actions": ["update"],
  "locations": ["https://api.example.com/posts"]
}
```

### Delegation chains [#delegation-chains]

Chain verification belongs to the token layer. `permdock/jwt` checks that a nested RFC 8693 `act` claim is well formed, takes the outermost `act.sub` as the `actor` (RFC 8693 section 4.1: the current actor; nested `act` claims are prior actors and never decide access), and records the whole chain as `delegation.chain` for audit; a malformed chain yields the anonymous subject. Core trusts the `scopes` and `authorization_details` the verified token carries.

### Calls with no human principal [#calls-with-no-human-principal]

A service agent acting on its own behalf is a principal with `kind: 'service'` and its own roles, not a separate subject shape. Between services inside one trust domain, an AuthZEN call carries the decision; PermDock defines no transaction token profile.

### Attenuation invariants [#attenuation-invariants]

* A delegation may only remove or narrow; a grant that the principal lacks cannot be added by any `authorization_details` entry, `actor_token` or chain hop.
* An empty `scopes` list is no delegated authority: every check is `denied` with reason `no-delegation`. An `actor` with no `delegation` at all is denied the same way; a subject with no `actor` is a human call and is not narrowed by delegation.

### What PermDock does not do [#what-permdock-does-not-do]

PermDock does not issue tokens, run token exchange, validate DPoP proofs or drive CIBA. Those belong to the authorization server and the transport layer. PermDock consumes their output.

## Mapping table [#mapping-table]

| OAuth concept | PermDock concept |
| --- | --- |
| Resource owner / user in the token | `principal` |
| `client_id`, `act` claim, `actor_token`, `requested_actor` | `actor` |
| OAuth `scope` | `delegation.scopes`, matched against `permission.scope` |
| RFC 9396 `authorization_details` | `delegation.authorizationDetails`; `type` matched against the resource name, `actions` against the action |
| RAR constraint payload | Enforced by the authorization server; the grant's own condition still applies |
| Agent Delegation Chain hops, RFC 8693 `act` | `delegation.chain`, verified by the token layer and recorded for audit |
| Monotonic attenuation | Structural: decision = principal grants ∩ delegation |
| RFC 8693 token exchange, ID-JAG | Transparent; resulting token's claims feed `principal`, `actor`, `delegation` |
| DPoP, CIBA | Transport and consent layer; CIBA is one way `approval-required` can be fulfilled |
| Transaction tokens | Not used; services call each other's AuthZEN endpoint |
| Service agent with no human | `principal` with `kind: 'service'` |

## Sources [#sources]

* [Agent Delegation Chain draft](https://datatracker.ietf.org/doc/html/draft-asor-wimse-agent-delegation-chain-00).
* [Credential Delegation Protocol draft](https://datatracker.ietf.org/doc/draft-sweeney-wimse-credential-delegation/).
* [OAuth for AI agents on behalf of a user draft](https://datatracker.ietf.org/doc/html/draft-oauth-ai-agents-on-behalf-of-user-02).
* [MCP Enterprise-Managed Authorization](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/main/docs/extensions/auth/enterprise-managed-authorization.mdx).
* RFC 9396, RFC 8693, RFC 9449 and RFC 7523 are referenced by number; see the drafts above for how they compose.
* [two-principal subject](/docs/concepts/subject).
