Agent as OAuth Client¶
Auth0's core modeling choice for agentic identity: the agent is a client, the MCP server is also a client, and upstream APIs remain traditional OAuth resource servers.
Why this framing works¶
Existing OAuth/OIDC infrastructure already distinguishes client (the app requesting access), resource server (what it accesses), and resource owner (the user). Decades of tooling — scopes, refresh tokens, PKCE, token introspection, audience binding, actor claims — all apply when you map "agent" to "client" and "MCP server" to "client + resource server hybrid."
No new cryptography. No new protocols. Just slightly more complex topologies of the same primitives.
The topologies¶
- Simple agent, upstream API. Agent = client of user-facing app; agent also = client of upstream (via token-vault token exchange).
- Agent + MCP server. Agent = client of MCP; MCP = client of upstream. Two token exchanges; MCP server registered via dynamic-client-registration.
- Agent-to-agent. One agent = client of another agent's service. Actor claims track the delegation chain.
Actor / authorizing-party claims¶
When user delegates to agent which delegates to upstream, the access token's azp (authorizing party) claim identifies the agent, while sub identifies the user. This lets the resource server distinguish:
- Is the request coming from the user directly, or via an agent?
- If the agent misbehaves, who do I revoke — the user or the agent?
Galan: "If an agent goes wrong, I want to know the agent goes wrong. You can set any policy on that."
Scope propagation¶
Scopes requested during token exchange are bounded by the scopes on the original connection. Nothing the LLM asks for that wasn't pre-authorized can appear in the access token. Authorization is in the SDK (code), not in the LLM reasoning loop — prompt injection cannot escalate.
Cross-references¶
- auth-for-ai-four-pillars — parent
- token-vault — delegated upstream access
- async-auth-ciba — risky-operation approval
- dynamic-client-registration — MCP-specific flow
- ai-generated-code-is-untrusted — why scopes-in-code matters