laravel-iam-agents
Delegated access for AI agents — the optional laravel-iam-server
module that lets an AI agent act on behalf of a user without ever holding the user’s token.
The invariant
Everything in this module enforces one sentence:
A delegated token carries two identities (
sub= the user,act= the agent), and effective
authority is the strict intersection of what the user may do and what the agent may do —
never the union, evaluated fresh by the PDP, fail-closed.
{ "iss": "https://iam.example.com", "sub": "user:42",
"act": { "sub": "agent:01J8XKQ0V2" },
"aud": "mcp://crm-tools", "scope": "orders:read orders:draft",
"pds_dgr": "dgr_01J9…", "exp": 1756000300 }
Why not just hand the agent the user’s token?
Because then the agent is the user: every permission, for the token’s whole life, indistinguishable
in every log. If the agent is manipulated (prompt injection is an input problem — you cannot prompt
it away), the blast radius is the user’s entire account. Delegation shrinks that to: the scopes the
user consented to ∩ the permissions the agent itself has — for ≤ 5 minutes at a time, revocable
in one click, with both identities in every audit record.
What the module ships
| Piece | What it does |
|---|---|
| Agent registry | Agents as first-class subjects (agent:{ulid}), lifecycle pending → active → suspended/retired, private_key_jwt only |
| Delegation grants | The user’s consent: agent, scopes, purpose, expiry, consent evidence (AAL + confirmation id), revocable |
| RFC 8693 grant | Token exchange on the server’s own /oauth/token — registered from the outside, no core fork |
| Intersection PDP | checkDelegated() decorator: user check ∧ agent check ∧ grant still active, deny-overrides |
| PSD2-grade consent | Step-up bound to (agent, scopes, ttl, purpose) — parameters changed after the screen ⇒ refused |
| Gated agentic registration | RFC 7591 DCR + auth.md/ID-JAG discovery — always lands pending, human approves |
Audit stream=delegation |
Every exchange (issued and refused), grant create/revoke, lifecycle transition — sealed in the server’s hash chain and pushed to webhooks |
Where to go next
- New to delegation? Read the Glossary — five terms, concrete examples.
- Want it running? The Quickstart goes from
composer requireto a delegated
call in five minutes. - Reviewing security? Threat model & negative tests — what we refuse,
proven by tests. - Something failing? Troubleshooting explains every RFC 8693 error
this server emits, one by one.