Authentication
Agent Identity
First-class agent principals, audit trails, and the actor chain
Last verified 2026-09-25
Summary
Rather than running all agents under a shared service account, give each agent its own principal with a unique encoding its name, task, and permission ceiling. Sub-agents cannot exceed their parent's scope via . Audit logs capture both agent_id and actor_chain for full traceability.
Orchestrator (own credentials)
├── Sub-Agent A (own credentials, scoped)
└── Sub-Agent B (own credentials, scoped)- Each agent is a first-class principal with distinct credentials
- Token Exchange for delegation with scope narrowing and audience restriction
- includes
sub,act(actor chain),agent_name,task_id,parent_task_id - Audit logging captures agent identity and the full delegation chain
- Human instruction is threaded through all downstream actions
When a human calls an , you know who they are: user ID, organization, IP. When an agent calls, the picture is fuzzier. You see the client, but which agent within the deployment made this call? What task? What human instruction triggered it? RFC 8693 Token Exchange and JWT act (actor) claims establish agents as first-class principals with independent credentials, bounded scopes, and full audit trails.
The Identity Hierarchy
Traditional approach: all agent traffic under one service account.
Human ──→ Service Account ──→ API
Agent ────→ (same account)Modern approach: each agent has its own identity and scoped credentials.
Orchestrator Agent (own credentials)
├── Sub-Agent A (own credentials, scoped)
└── Sub-Agent B (own credentials, scoped)Every agent gets credentials encoding its name, task, and permission ceiling. A cannot exceed its parent's scope. If the parent holds invoices:read, the sub-agent can receive at most invoices:read via Token Exchange - it cannot manufacture broader permissions.
Token Exchange for Delegation
RFC 8693 Token Exchange enables the to derive narrow, time-limited tokens for sub-agents. The full request/response implementation lives on Token Exchange - what matters here is the identity shape it produces:
- Narrower scope (e.g.,
invoices:readonly, not*) - Shorter lifetime (15 min vs 1 hour)
- Audience-restricted (only accepted by the target service)
- Actor chain via the
actclaim
See Token Exchange for the full details.
JWT Claims for Agent Identity
Trust identity claims in the JWT, not in custom headers. The token should include:
{
"iss": "https://auth.example.com/",
"aud": "https://invoices-service.example.com/",
"sub": "orchestrator_agent",
"act": {
"sub": "sub_agent_a"
},
"agent_name": "InvoiceSummaryOrchestrator",
"agent_version": "1.0.0",
"task_id": "task_abc123",
"parent_task_id": "task_parent_xyz",
"org_id": "org_acme",
"scope": "invoices:read",
"exp": 1750000000
}sub- the calling agent identityact- optional actor chain for nested delegation (agent → agent → service)agent_name- human-readable agent identifier (for logs)task_id- correlates all actions from this executionparent_task_id- links to parent task in multi-agent chainsorg_id- tenant isolation
The act claim is how you who actually made a request. If agent A delegates to agent B, the token includes both identities.
Audit Logging
Log both sub (agent making the request) and act (if any) on every action:
// Walk the nested `act` chain (agent -> agent -> ... -> service) into a flat array
function flattenActorChain(act?: { sub: string; act?: { sub: string } }): string[] {
const chain: string[] = [];
let current = act;
while (current) {
chain.push(current.sub);
current = (current as { act?: { sub: string } }).act;
}
return chain;
}
async function logAction(req: Request, action: string) {
const token = await validateToken(req.headers.get("authorization"));
auditLog.write({
timestamp: new Date().toISOString(),
agent_id: token.sub,
agent_name: token.agent_name,
agent_version: token.agent_version,
actor_chain: flattenActorChain(token.act),
task_id: token.task_id,
parent_task_id: token.parent_task_id,
org_id: token.org_id,
action: action,
scope: token.scope,
ip: req.ip,
});
}Include the original human instruction if available:
{
"timestamp": "2025-04-17T10:22:01.432Z",
"agent_id": "orchestrator_agent",
"agent_name": "InvoiceSummary",
"task_id": "task_abc123",
"human_instruction": "Summarize outstanding invoices for Q1 2025",
"action": "list_invoices",
"resource": "/invoices",
"method": "GET",
"response_status": 200,
"duration_ms": 142,
"org_id": "org_acme"
}From this log, an auditor can reconstruct exactly what happened: which agent, what task, what instruction, when, and with what result.
X-Agent-Request Header (Informational)
Agents may send X-Agent-Request: true to signal agent-originated traffic, but do not rely on this for security:
curl -H "Authorization: Bearer ..." \
-H "X-Agent-Request: true" \
-H "X-Agent-Name: InvoiceSummary/1.0" \
https://api.example.com/invoicesAny client can set this header. Use it only for non-security purposes: returning structured error responses, suppressing interactive UI elements, adjusting . For identity claims, use the JWT.
Payment Mandates
A is a specialization of agent identity. Instead of proving who the agent is, it proves the agent is authorized to make a specific payment on a user's behalf. AP2-style signed intent binds an agent, a user, an amount cap, and a merchant scope into a single verifiable credential - the same scoped, delegated, auditable pattern as the actor chain above, applied to spending authority rather than API access. The token-narrowing and act-claim machinery on this page is what a mandate specializes: authority that a downstream merchant or payment network can verify without trusting the caller's self-description. See Payments for how , , and serialize this primitive.
Web Bot Auth
Web Bot Auth uses HTTP Message Signatures to associate automated requests with a verifiable sender. The sender or its identity provider publishes verification keys through a signatures directory. The receiving service validates the signed request, key ownership and validity, covered message components, and replay protections before applying its bot-access policy. A receiving website does not become a verified sender merely by publishing an empty directory.
The IETF chartered a Web Bot Auth working group, which adopted draft-ietf-webbotauth-httpsig-protocol on 2026-09-01, replacing the earlier individual submission (draft-meunier). The protocol it defines: a Signature-Agent header for in-band key discovery, a JWKS-format key directory served from a , and a trust model that binds identity to an HTTPS URL rather than to individual keys - so key rotation doesn't break the trust relationship. Treat it as an active working-group draft, not yet a published RFC.
Request identity does not grant a user's application permissions. Keep OAuth, resource authorization, and spending authority independent. A User-Agent string is a useful traffic hint, not proof of identity. Use signed traffic where the intended sender and receiver support a compatible profile; test rejected signatures and expired keys as well as successful requests. See Cloudflare's signing, verification, and directory examples, RFC 9421, and the directory reference.
Workload Identity for Agents
Web Bot Auth answers "who sent this request." A separate, still-forming line of standards work answers "what workload is this agent running as, and how does it prove that to another service." The MCP roadmap (2026-08-22) names this directly: authorization today assumes a human clicking through a browser consent screen, which doesn't fit an agent running unattended as a cloud workload. Its stated direction is to finalize and drive adoption (see DPoP), and to define an opinionated path for agent identity and delegation through Workload Identity Federation, building on the grant used in enterprise-managed authorization - with the explicit goal of engaging the IETF OAuth and WIMSE working groups rather than inventing an MCP-specific mechanism.
WIMSE (Workload Identity in Multi-Service Environments) is the IETF working group this points to. Its architecture draft (-08, 2026-07-06) defines how workloads obtain and present identity credentials - X.509 certificates or JWTs - to authenticate to each other within a trust domain. A separate document, draft-ni-wimse-ai-agent-identity, applies these ideas specifically to . It remains an individual submission with no working-group standing and has lapsed (last revision February 2026) - read it as a signal of where this is headed; don't build against it yet.
None of this is ready to implement today. The concrete, shippable pieces are DPoP (independent of MCP, see DPoP) and Token Exchange with actor-chain claims, both covered above. Track Workload Identity Federation and WIMSE-based agent identity; don't build on them yet.
Checklist
- Agents have distinct principals, not a shared service account
- Token Exchange is used to derive sub-agent tokens with narrower scope
- Sub-agent scopes cannot exceed parent scope
- JWT includes
sub,act,agent_name,task_id,parent_task_id - Audit logs include both
agent_idandactor_chain(for delegation) - Human instruction is captured and threaded through all downstream actions
- Parent task ID is propagated on every sub-agent call
See Also
- Token Exchange - RFC 8693 implementation
- OAuth 2.1 for Agents - token issuance
- DPoP - the MCP roadmap's near-term identity priority
- auth.md - user-bound agent registration
- RFC 8693: OAuth 2.0 Token Exchange - delegation and narrowing
- MCP roadmap - DPoP and Workload Identity Federation direction
- WIMSE architecture - workload identity trust domains this direction builds on