> ## Documentation Index
> Fetch the complete documentation index at: https://www.truefoundry.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Agent Identity and Governance

> How TrueFoundry identifies agents, governs agent-to-agent and agent-to-MCP calls, and carries identity across hops using token exchange (ID-JAG, OBO).

When a single agent calls one tool, identity is simple: the user is in front of the screen, and the tool runs as that user. That model breaks the moment agents start calling other agents and MCP servers on their own. A planning agent calls a research agent, which calls a retrieval agent, which calls three MCP servers. By the third hop, two questions have no clear answer: **who is this call really for**, and **who is allowed to make it**.

Agent Identity and Governance answers both. It gives every agent a verifiable identity, lets you define who each agent can act for and who is allowed to call it, and carries the original user's identity across every hop using standard OAuth token exchange — so the MCP server at the end of the chain still enforces the right permissions for the right person.

<Info>
  This page describes the identity and governance model for the [MCP Gateway](/docs/ai-gateway/mcp/mcp-overview) and the Agent Gateway. It builds on concepts you should know first: [MCP Gateway authentication](/docs/ai-gateway/mcp/mcp-gateway-auth-security), [Identity Providers](/docs/platform/identity-providers), and [Virtual Accounts](/docs/platform/virtual-account-management). Some capabilities described here are rolling out incrementally — talk to your TrueFoundry contact for current availability.
</Info>

## The governance problems

As soon as teams build agents that call other agents and tools on their own, a cluster of governance problems appears that a normal API gateway cannot solve. They are introduced once here and referenced throughout the page.

<AccordionGroup>
  <Accordion title="Shadow agents: no central registry or enforcement" icon="ghost">
    Teams build agents on whatever they like — LangGraph, CrewAI, Bedrock, a homegrown HTTP service, or a copilot embedded in a SaaS product. Nothing forces them to declare those agents anywhere, so security has no inventory of what exists, who owns it, or what it can reach.

    **Example:** A growth team ships a CrewAI agent that queries the Salesforce MCP server with a shared API key. It works, but no central system knows the agent exists — so it can't be discovered, audited, rate-limited, or shut off.

    What's needed is a chokepoint: an agent cannot reach any MCP server or any other agent unless it presents a **registered identity**. That turns registration into enforcement and gives you an organization-wide inventory of every agent, regardless of the framework it was built on.
  </Accordion>

  <Accordion title="No way to identify the caller" icon="question">
    When a request reaches an MCP server or an agent, the receiver needs to know whether the caller is a human, a service, or another agent — and *which* agent. Without a verifiable agent identity you cannot apply policy, attribute usage, or stop an unapproved agent from calling a sensitive tool.
  </Accordion>

  <Accordion title="One-size-fits-all MCP policies" icon="sliders">
    Access to an MCP server is usually decided by the *user's* permissions alone. But one user can drive many agents, and they shouldn't all get the same reach.

    **Example:** Jane uses both a support copilot and an engineering agent. On the Jira MCP server you want the support copilot to **read** issues only, while the engineering agent may **read and write** — even though both act on Jane's behalf. With user-only policy, the server just sees "Jane" in both cases and grants Jane's full permissions regardless of which agent is driving.

    What's needed is policy keyed on the **(agent, user, tool)** combination, so different agents acting for the same user get different, scoped-down access.
  </Accordion>

  <Accordion title="Lost identity across hops" icon="user-slash">
    The user authenticates to the first agent, but that agent calls a second agent, which calls an MCP server. Unless identity is deliberately carried forward, the MCP server sees nothing (and fails) or a shared service account (and over-grants). Per-user permissions, audit trails, and data isolation all collapse.

    **Example:** A planner agent calls a research agent that reads Confluence for Jane. By the second hop "Jane" is gone, so the research agent reads with a service account that can see *everyone's* pages — not just Jane's.
  </Accordion>

  <Accordion title="Over-broad tokens" icon="key">
    The simplest way to make calls work is to forward one powerful token everywhere — and the fastest way to a breach. A token minted for "Jane, in the planner agent" should not be replayable by a downstream agent against an unrelated MCP server. Tokens must be scoped down at every hop to exactly the audience and permissions required.
  </Accordion>

  <Accordion title="No unified audit or kill switch" icon="clipboard-list">
    When something goes wrong, you need to answer "which agent did this, on whose behalf, through which chain?" and revoke a single agent instantly — without taking everything else down.

    **Example:** A retrieval agent starts pulling data it shouldn't. You want to revoke just that agent's identity and see every call it made across every chain — not grep through five frameworks' logs to reconstruct what happened.
  </Accordion>
</AccordionGroup>

## Core concepts

Four ideas underpin the whole model. The rest of this page builds on them.

| Concept                              | What it is                                                                                                                                                                                       |
| ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **Agent Identity**                   | A first-class, verifiable identity for an agent — the credential an agent presents so the Gateway knows *which agent* is calling.                                                                |
| **Caller and callee**                | Every governed call has a caller (a user, a service/virtual account, or an agent) and a callee (an MCP server or another agent). Policy is defined per callee: who may call it, and for whom.    |
| **Acting on behalf of (delegation)** | An agent rarely acts for itself. It acts *on behalf of* a user. The model keeps the user identity and the agent identity distinct, and checks that the agent is authorized to act for that user. |
| **Token exchange**                   | At each hop, the Gateway exchanges the incoming token for a new token scoped to the next callee — preserving the user identity, recording the acting agent, and narrowing audience and scope.    |

### The entities that can make a call

Every governed call is attributed to one of three caller types. This is the same identity vocabulary used across TrueFoundry's [Identity and Access Management](/docs/platform/user-team-account-management).

| Caller type         | Represents                                                                                                               | Token it presents                                                                                         |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------- |
| **User**            | A human, resolved to a TrueFoundry user (directly or via an [Identity Provider](/docs/platform/identity-providers) JWT). | TrueFoundry PAT, or an IdP-issued JWT.                                                                    |
| **Virtual Account** | A service or application with no human behind it.                                                                        | A [Virtual Account](/docs/platform/virtual-account-management) token, or an IdP JWT that resolves to one. |
| **Agent Identity**  | A registered agent acting in an automated chain.                                                                         | An Agent Identity token (TrueFoundry-backed or IdP-backed).                                               |

The new entity is the **Agent Identity**. The other two already exist in the platform; agent identity slots in alongside them so that the same access-control engine can reason about all three.

## Two registrations: identity and agent

Agents involve two distinct registrations. They answer different questions, and most agents need both.

<Frame caption="An agent has an identity (who it is) and a registration (what it may do)">
  <img src="https://mintcdn.com/truefoundry/I_15w3VXjxbEnwEy/images/agent_identity_vs_registration.png?fit=max&auto=format&n=I_15w3VXjxbEnwEy&q=85&s=4c8e429fa5bd5d8a77a27d2fcf58369e" alt="Diagram showing an agent with an Agent Identity badge listing name, type, and credential on the left, and an Agent Registration document listing who can call, act on behalf of, tools allowed, and url to proxy on the right" width="1536" height="1024" data-path="images/agent_identity_vs_registration.png" />
</Frame>

|               | Agent Identity                                           | Agent registration                                                                              |
| ------------- | -------------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
| Answers       | *Who is this agent?*                                     | *What may this agent do, and how is it reached?*                                                |
| Required for  | **Every agent in your organization**                     | Agents that act on behalf of users and/or expose a proxyable URL                                |
| Defines       | The credential the agent presents and how it's validated | Who may call the agent, whom it may act on behalf of, and (if proxyable) the URL to proxy       |
| Managed under | **Access > Agent Identities**                            | **Agent Registry** (extends [Remote Agents](/docs/agent-platform/agent-registry/remote-agents)) |
| Spec `type`   | `agent-identity`                                         | `remote-agent`                                                                                  |

**Agent Identity is the mandatory baseline.** Every agent that touches the MCP Gateway or the Agent Gateway must present a registered agent identity. An agent that presents no identity — or one that isn't registered here — cannot reach any MCP server or any other agent.

<Check>
  **Registration is the enforcement point.** Because nothing can act without a registered identity, you get an organization-wide inventory of every agent — no matter which framework or platform it was built on — and nothing acts anonymously. This is what closes the [shadow-agent gap](#the-governance-problems).
</Check>

**Agent registration adds authorization and routing.** Once an agent has an identity, you register the agent itself to declare who may call it, whom it may act on behalf of, and — if it exposes a URL — where the Agent Gateway should proxy it. Agents embedded in third-party software that expose no callable URL still get a registration to carry this policy; they simply omit the proxy URL (see [The Agent Gateway](#the-agent-gateway)).

Why two and not one? Identity is *authentication* — proving which agent is calling — and is universal. The agent registration is *authorization and routing* — what the agent may do and how it's reached — and varies by deployment. Splitting them lets one identity be reused across registrations, and lets an agent be governed at the MCP Gateway even when it can never be proxied.

The next two sections define each spec in turn.

## Agent Identity

An Agent Identity is a registered, verifiable identity for an agent. It exists so that when an agent calls the MCP Gateway or the Agent Gateway, the Gateway can answer "which agent is this?" with cryptographic certainty rather than a guess based on a shared API key.

You register agent identities under **Access > Agent Identities** in the TrueFoundry UI.

<Frame caption="Creating a new Agent Identity under Access Management">
  <img src="https://mintcdn.com/truefoundry/zMKViw5QHAs-60av/images/new_agent_identity_form.png?fit=max&auto=format&n=zMKViw5QHAs-60av&q=85&s=386d3c3bce7d81c39a880fccd3db8a76" alt="New Agent Identity form showing Name, Identity backing type (TrueFoundry Backed or Identity Provider Backed), expiration date, auto rotation, notification, sync to secret manager, tags, and owning team fields" width="1024" height="583" data-path="images/new_agent_identity_form.png" />
</Frame>

### How an agent gets its identity

An agent identity can be backed two ways. The choice determines where the agent's token comes from and how it is validated.

<Tabs>
  <Tab title="TrueFoundry-backed">
    TrueFoundry issues and signs the token. This is the simplest option for agents you build and run yourself, and for getting started.

    * TrueFoundry mints a token bound to this agent identity.
    * The agent presents that token to the Gateway, which validates it directly.
    * The token can be auto-rotated, expired on a schedule, and synced to your secret manager so the agent can read it at runtime.

    Use this when the agent has no existing identity from your IdP, or when you want TrueFoundry to own the credential lifecycle.
  </Tab>

  <Tab title="Identity Provider-backed">
    The agent authenticates to *your* IdP (Okta, Entra ID, Auth0, etc.) and presents the IdP-issued JWT. TrueFoundry validates it through a configured [Identity Provider](/docs/platform/identity-providers) and resolves it to this agent identity.

    * The agent already has an identity in your IdP (for example, an Entra agent identity or an Okta service app).
    * TrueFoundry validates the JWT signature, issuer, and audience, then maps it to the agent identity.
    * This is the option that unlocks **token exchange** against your IdP — the Gateway can exchange the agent's IdP token for a downstream token (ID-JAG or OBO) that the target service trusts.
    * The IdP token can also be a **workload identity** issued by SPIFFE/SPIRE (a JWT-SVID), so the agent's credential is attested from its runtime rather than configured by hand. See the **Workload identity with SPIFFE and SPIRE** section below.

    Use this when you want your IdP to remain the source of truth and to centrally govern cross-application access.
  </Tab>
</Tabs>

<Accordion title="Workload identity with SPIFFE and SPIRE">
  An AI agent is, at bottom, an executable piece of software that runs *somewhere* — a Kubernetes pod, a container, a VM. Every one of those execution environments already has a way to identify the workloads running on it. Rather than minting and distributing a separate static credential for the agent, you can derive its identity from where it runs. This is **workload identity**, and [SPIFFE](https://spiffe.io/) (with its reference implementation [SPIRE](https://spiffe.io/docs/latest/spire-about/spire-concepts/)) is the open standard for it.

  SPIFFE fits agents naturally: the agent doesn't hold a long-lived secret at all. Instead, the platform attests the running workload and issues it a short-lived, automatically rotated credential.

  <Steps>
    <Step title="The platform attests the workload">
      SPIRE performs **node attestation** (proving which machine or node the workload runs on, using evidence like a Kubernetes projected service account token, an AWS instance identity document, or GCP instance metadata) and **workload attestation** (proving which process is calling, using selectors like the Kubernetes service account, namespace, or container image digest). Only a workload that matches a registered policy gets an identity.
    </Step>

    <Step title="SPIRE issues an SVID">
      The attested workload receives a SPIFFE Verifiable Identity Document (SVID) carrying its SPIFFE ID, for example `spiffe://acme.com/ns/agents/sa/research-agent`. SVIDs come in three forms:

      | SVID type      | Format                                        | Notes                                                                                                                                                                                                            |
      | -------------- | --------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
      | **X.509-SVID** | X.509 certificate (SPIFFE ID in the SAN)      | Used for mTLS; authenticates a channel.                                                                                                                                                                          |
      | **JWT-SVID**   | Signed JWT                                    | Bearer token for HTTP/`Authorization` headers; the practical fit for gateway calls today.                                                                                                                        |
      | **WIT-SVID**   | Key-bound JWT (WIMSE Workload Identity Token) | Emerging [IETF WIMSE](https://datatracker.ietf.org/doc/draft-ietf-wimse-s2s-protocol/) profile; requires proof-of-possession, so it resists replay through intermediaries like gateways and agent orchestrators. |

      SVIDs are short-lived (often an hour or less) and rotated by the SPIRE agent before they expire, so the agent never experiences a credential-expiry event.
    </Step>

    <Step title="TrueFoundry validates it as an Identity Provider">
      SPIRE can expose an OIDC-compliant endpoint. Register it as a TrueFoundry [Identity Provider](/docs/platform/identity-providers) (issuer, audiences, JWKS URI) and map the SPIFFE ID claim to an agent identity. The agent then presents its JWT-SVID to the Gateway exactly like any other IdP token — and the same [token exchange](#token-exchange-the-mechanism-that-carries-identity) (ID-JAG / OBO) flows apply on the outbound side.
    </Step>
  </Steps>

  <Tip>
    A common Kubernetes pattern: SPIRE trusts the cluster's projected service account tokens, exchanges them for SPIFFE credentials, and the agent reads its JWT-SVID from the SPIFFE Workload API at runtime. No secret is ever written to a config file or image.
  </Tip>
</Accordion>

<Accordion title="Per-agent vs. per-instance identity, and sender-constrained tokens">
  Two refinements matter once agents run in production:

  * **Per-agent vs. per-instance.** The agent identity you register is the *stable, per-agent* principal — what you govern: ownership, policy, inventory, audit. At runtime, each running *instance* can additionally receive a short-lived, attested credential (a SPIFFE SVID, for example) bound to that stable identity. The per-agent identity gives you policy and inventory; the per-instance credential gives you attestation and a **surgical kill switch** — revoke one rogue instance without invalidating the whole fleet. This directly addresses the [kill-switch problem](#the-governance-problems).
  * **Sender-constrained tokens.** A bearer token can be replayed by anyone who steals it — a real risk for agents that can be prompt-injected or memory-exfiltrated. Where the target supports it, bind the token to the sender with [mTLS (RFC 8705)](https://www.rfc-editor.org/rfc/rfc8705.html) or [DPoP (RFC 9449)](https://www.rfc-editor.org/rfc/rfc9449.html), or use a key-bound WIT-SVID, so a stolen token is useless without the private key. Where it doesn't, terminate at the Gateway, which holds the stronger credential boundary on the agent's behalf.
</Accordion>

### Agent identity spec

The form maps to a YAML spec you can apply directly (the **Apply using YAML** option on the form). The example below shows both backing types and the delegation policy.

<CodeGroup>
  ```yaml TrueFoundry-backed theme={"dark"}
  type: agent-identity
  name: research-agent
  description: Identity for the research agent used across the org
  owned_by_team: data-platform
  identity:
    type: truefoundry-backed
    expiration_date: "2026-12-31"        # optional; omit for a non-expiring token
    auto_rotation:
      enabled: true
      rotate_before: 7d                  # rotate this long before expiry
    notification:
      enabled: true
    sync_to_secret_manager:
      enabled: true
      secret_store: tfy-secret-store     # a connected secret store
      path: agents/research-agent/token
  tags:
    owner: data-platform
    env: production
  ```

  ```yaml IdP-backed (incl. SPIFFE) theme={"dark"}
  type: agent-identity
  name: research-agent
  owned_by_team: data-platform
  identity:
    type: idp-backed
    provider: my-okta-provider           # a configured Identity Provider
    subject_claim: sub                   # claim that identifies this agent
    subject: agent:research-agent@acme.com
    # Optional: derive the credential from a SPIFFE workload identity
    spiffe:
      enabled: true
      trust_domain: acme.com
      spiffe_id: spiffe://acme.com/ns/agents/sa/research-agent
      svid_type: jwt-svid                # x509-svid | jwt-svid | wit-svid
  tags:
    owner: data-platform
    env: production
  ```
</CodeGroup>

<Accordion title="Agent identity field reference">
  | Field                                         | Required | Description                                                                                                                                              |
  | --------------------------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
  | `type`                                        | Yes      | Always `agent-identity`.                                                                                                                                 |
  | `name`                                        | Yes      | Unique, human-readable name. Used in policies, traces, and audit logs.                                                                                   |
  | `description`                                 | No       | Short description of the agent's purpose.                                                                                                                |
  | `owned_by_team`                               | Yes      | The team that owns and can manage this identity.                                                                                                         |
  | `identity.type`                               | Yes      | `truefoundry-backed` (TrueFoundry issues the token) or `idp-backed` (your IdP issues it).                                                                |
  | `identity.expiration_date`                    | No       | Expiry for a TrueFoundry-backed token. Omit for a non-expiring token governed by rotation.                                                               |
  | `identity.auto_rotation`                      | No       | Rotate the token automatically before expiry. Recommended for production.                                                                                |
  | `identity.notification`                       | No       | Notify owners ahead of expiry or rotation events.                                                                                                        |
  | `identity.sync_to_secret_manager`             | No       | Store the token in a connected [secret store](/docs/ai-gateway/secret-manager-in-ai-gateway) so the runtime can read it directly.                        |
  | `identity.provider`                           | IdP only | The configured [Identity Provider](/docs/platform/identity-providers) that validates the agent's JWT.                                                    |
  | `identity.subject_claim` / `identity.subject` | IdP only | The JWT claim and value that resolve the token to this agent identity.                                                                                   |
  | `identity.spiffe`                             | No       | Bind the identity to a SPIFFE workload identity (`trust_domain`, `spiffe_id`, `svid_type`). See the **Workload identity with SPIFFE and SPIRE** section. |
  | `tags`                                        | No       | Key-value pairs to categorize the identity.                                                                                                              |
</Accordion>

<Note>
  An agent identity carries no authorization policy — it only establishes *who the agent is*. What an agent may do (whom it can act for, who can call it) is defined on its [agent registration](#agent-registration), below.
</Note>

## Agent registration

The agent registration is the governance and routing record for an agent. Where the [agent identity](#agent-identity) says *who the agent is*, the registration says *what it may do and how it's reached*. It extends the existing [Remote Agent](/docs/agent-platform/agent-registry/remote-agents) registration.

| Setting                               | Description                                                                                                                                                             |
| ------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Associated agent identity**         | The [agent identity](#agent-identity) this agent presents when it, in turn, calls other agents or MCP servers downstream.                                               |
| **Who can call this agent**           | The callers permitted to invoke it: users, virtual accounts, and **other agents** (by their agent identity).                                                            |
| **On whose behalf the agent can act** | The users and teams this agent may act for when it makes downstream calls. This is the delegation allow-list, enforced at both the Agent Gateway and the MCP Gateway.   |
| **URL to proxy**                      | The endpoint the Gateway forwards requests to. Omit it for agents that can't be proxied (a governance-only registration — see [The Agent Gateway](#the-agent-gateway)). |

### Agent spec

```yaml theme={"dark"}
type: remote-agent
name: research-agent
description: Researches topics and returns summaries
owned_by_team: data-platform
# Identity this agent presents on its own downstream calls
identity: agent-identity:research-agent
# Who is allowed to call this agent
callers:
  users:
    - jane@acme.com
  teams:
    - support
  virtual_accounts:
    - data-pipeline
  agents:
    - agent-identity:planner-agent     # another agent, by its identity
# Whom this agent may act on behalf of when it makes downstream calls
act_on_behalf_of:
  users:
    - jane@acme.com
  teams:
    - support
# Endpoint the Agent Gateway proxies to. Omit for a governance-only
# registration (an embedded agent with no callable URL).
url: https://research-agent.internal.acme.com
framework: a2a                         # a2a | custom (only when url is set)
agent_card_path: /.well-known/agent-card.json
# How the Gateway authenticates to the remote agent endpoint
auth:
  type: token_exchange                 # token_exchange | token_passthrough | header
```

<Accordion title="Agent field reference">
  | Field              | Required | Description                                                                                                                               |
  | ------------------ | -------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
  | `type`             | Yes      | `remote-agent` for an agent registration.                                                                                                 |
  | `name`             | Yes      | Unique name for the agent in the registry.                                                                                                |
  | `description`      | Yes      | Short description of the agent's purpose.                                                                                                 |
  | `owned_by_team`    | Yes      | The team that owns and can manage the registration.                                                                                       |
  | `identity`         | Yes      | The [agent identity](#agent-identity) this agent presents on its own downstream calls.                                                    |
  | `callers`          | Yes      | Who may invoke this agent: `users`, `teams`, `virtual_accounts`, and `agents` (by identity).                                              |
  | `act_on_behalf_of` | No       | The users and teams this agent may act on behalf of, enforced before any delegated token is minted. Required if the agent acts for users. |
  | `url`              | No       | The endpoint the Gateway proxies to. Omit for agents that can't be proxied (governance-only).                                             |
  | `framework`        | url only | `a2a` or `custom`. See [Supported Frameworks](/docs/agent-platform/agent-registry/remote-agents#supported-frameworks).                    |
  | `agent_card_path`  | A2A only | Path to the agent card JSON (default `/.well-known/agent-card.json`).                                                                     |
  | `auth.type`        | No       | Outbound auth to the remote endpoint: `token_exchange` (mint a scoped delegated token), `token_passthrough`, or `header`.                 |
</Accordion>

The decision logic that uses this registration lives in [The Agent Gateway](#the-agent-gateway).

## Token exchange: the mechanism that carries identity

Everything in this page rests on one mechanism: **token exchange**. Rather than forwarding the same powerful token everywhere, the Gateway trades the incoming token for a *new* token at each hop. The new token is scoped to exactly the next callee and records who is acting for whom.

Token exchange is defined by [RFC 8693](https://www.rfc-editor.org/rfc/rfc8693.html). The Gateway (or your IdP, acting as a Security Token Service) takes a `subject_token` and returns a new token for a target `audience` and `scope`.

<Frame caption="Token exchange at a single gateway: the subject (user) is preserved, the acting agent is recorded, and the scope is narrowed to the next callee">
  <img src="https://mintcdn.com/truefoundry/I_15w3VXjxbEnwEy/images/agent_token_exchange.png?fit=max&auto=format&n=I_15w3VXjxbEnwEy&q=85&s=b409a4a3339080ff3dcd01209aa507bc" alt="Diagram of token exchange at a gateway: an incoming credential for user Jane bound for Agent A enters the gateway, which performs a token exchange and emits a smaller token for user Jane with actor Agent A, audience Jira, and scope read. A legend notes the subject is preserved and the scope is narrowed" width="1536" height="1024" data-path="images/agent_token_exchange.png" />
</Frame>

### Delegation vs. impersonation

RFC 8693 supports two semantics. The distinction matters for agents, and TrueFoundry uses **delegation** by default.

|                                  | Impersonation                         | Delegation                                         |
| -------------------------------- | ------------------------------------- | -------------------------------------------------- |
| Resulting token says             | "I *am* the user."                    | "I am acting *on behalf of* the user."             |
| Acting party visible downstream? | No — indistinguishable from the user. | Yes — recorded in an `act` (actor) claim.          |
| Audit trail                      | The agent disappears.                 | Full chain: user + each acting agent.              |
| TrueFoundry default              | —                                     | **Used by default**, so every hop is attributable. |

With delegation, the issued token is a **composite token**: its subject (`sub`) stays the original user, and an `act` claim names the agent acting for them. Chained calls produce nested `act` claims — a complete, verifiable record of who touched the request.

```json theme={"dark"}
{
  "sub": "user-jane@acme.com",
  "aud": "https://mcp.internal/jira",
  "scope": "issues.read issues.write",
  "act": {
    "sub": "agent:research-agent",
    "act": {
      "sub": "agent:planner-agent"
    }
  }
}
```

### Who is allowed to act for whom

Token exchange is only safe if the authorization server refuses exchanges that aren't permitted. Two claims govern this:

* **`may_act`** — present on the user's token (or in policy). It declares which actors are allowed to act on behalf of this subject. The Gateway checks this before issuing a delegated token.
* **`act`** — present on the issued token. It records which actor actually performed the exchange.

In TrueFoundry, the "may act" decision is configuration you define: each [agent registration](#agent-registration) declares the **users and teams it may act on behalf of**, and each callee declares **which agents may call it**. The Gateway enforces both before any token is minted.

<Tip>
  **Authorize on the current actor, audit on the full chain.** The nested `act` claims are an informational history for forensics. Authorization decisions are made on the *current* actor plus the subject plus policy context — not on the historical chain. Keep the whole chain for audit; decide on the top of it.
</Tip>

<Note>
  **On the horizon — verifiable actor chains.** In RFC 8693 today, the prior actors in a nested `act` chain are *informational* — a downstream service can't independently verify them cryptographically. The IETF [SPICE actor-chain draft](https://datatracker.ietf.org/doc/draft-mw-spice-actor-chain/) profiles a signed commitment per hop so each step is provable, enabling forensic reconstruction of "agent A called B called C" without trusting any single intermediary. As this matures, TrueFoundry's exchange can emit verifiable chains with no change to how you write policy.
</Note>

### Authority reduction, not amplification

A common worry with delegation is that an agent might request *more* than the user granted — privilege escalation by the back door. It can't. A token exchange never grants authority based on what is asked for; it grants the **intersection** of three independent limits:

1. what the **user** is allowed to do,
2. what the **agent** is allowed to do, and
3. what the **target** (MCP server or agent) is willing to accept.

Requesting a scope is only an input to that decision. If the user lacks the permission, or the agent isn't permitted to act with it, or the target won't accept it, the exchange fails. Each hop therefore produces a token that is *more* constrained than the last — narrower scope, a single audience, and a recorded actor.

This cuts both ways, which is the point: an agent may hold a capability the user never had (and the user context still bounds it), and a user may hold a permission the agent should never exercise (and the agent's allow-list still bounds it). Delegation makes those boundaries explicit instead of silently inherited.

### How this maps to your IdP

The same RFC 8693 foundation is profiled differently by each identity provider. TrueFoundry adapts to whichever your agent identity is backed by.

| Capability           | Generic (RFC 8693)                              | Okta — ID-JAG / Cross-App Access                                 | Microsoft Entra — On-Behalf-Of                        |
| -------------------- | ----------------------------------------------- | ---------------------------------------------------------------- | ----------------------------------------------------- |
| Underlying standard  | Token Exchange (RFC 8693)                       | RFC 8693 + JWT Bearer (RFC 7523)                                 | RFC 8693 profiled as OBO                              |
| Number of exchanges  | 1                                               | 2 (get ID-JAG, then redeem it)                                   | 2 (blueprint token, then OBO)                         |
| Intermediate token   | New access token                                | **ID-JAG** (`urn:ietf:params:oauth:token-type:id-jag`)           | Federated/exchange token, then resource token         |
| Grant to resource    | `token-exchange`                                | `urn:ietf:params:oauth:grant-type:jwt-bearer`                    | `jwt-bearer` with `requested_token_use=on_behalf_of`  |
| Carries acting agent | `act` claim                                     | ID-JAG audienced to the resource's authorization server          | `act` / `xms_act_fct` actor-facet claims              |
| Best for             | Internal services that trust the Gateway as STS | Cross-app and cross-vendor access brokered by the enterprise IdP | Microsoft-centric estates with Entra agent identities |

<AccordionGroup>
  <Accordion title="Okta — ID-JAG (Cross-App Access)" icon="key">
    [Cross-App Access (XAA)](https://oauth.net/cross-app-access/), built on the [Identity Assertion JWT Authorization Grant](https://datatracker.ietf.org/doc/draft-ietf-oauth-identity-assertion-authz-grant/) draft, lets the enterprise IdP broker access between two applications without a per-user consent click. It is a **two-step** exchange:

    1. The caller presents an identity assertion (an ID token, or a refresh token) to the IdP's token endpoint with `requested_token_type=urn:ietf:params:oauth:token-type:id-jag` and `audience` set to the target resource's authorization server. The IdP returns an **ID-JAG** — a short-lived, signed grant that names the user and is audienced to the resource.
    2. The caller redeems the ID-JAG at the resource's authorization server using `grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer`. The resource server applies its own policy and returns an access token.

    The resource authorization server stays in control — it decides whether to honor the ID-JAG and what scopes to grant. This is the right model when the inbound token is an IdP token and the outbound call needs a token the **MCP server's own authorization server** trusts. There is an [MCP-specific profile of XAA](https://oauth.net/cross-app-access/) for exactly this case.
  </Accordion>

  <Accordion title="Microsoft Entra — On-Behalf-Of (OBO)" icon="key">
    Microsoft Entra Agent ID profiles RFC 8693 as the **On-Behalf-Of** flow with an agent-aware twist. An *agent identity blueprint* (the parent registration) holds the credential and declares delegated permissions; *agent identities* are children of that blueprint.

    1. The client authenticates the user and obtains a user access token audienced to the blueprint.
    2. The agent identity calls Entra's token endpoint with `grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer`, `requested_token_use=on_behalf_of`, the user token as the `assertion`, and its own client assertion.

    Entra validates the audience chain and returns a downstream token whose subject is still the user, with **actor-facet claims** (`act`, `xms_act_fct`) identifying the agent. The result is the same composite shape: user as subject, agent as actor.
  </Accordion>

  <Accordion title="No IdP support? Token passthrough and scoped TrueFoundry tokens" icon="shuffle">
    If a target MCP server or agent cannot participate in token exchange, TrueFoundry falls back gracefully:

    * **TrueFoundry-issued scoped token** — when the agent identity is TrueFoundry-backed, the Gateway itself acts as the STS and mints a scoped, delegated token audienced to the next callee. This brings the benefits of exchange even when your IdP isn't in the path. **Prefer this.**
    * **Token passthrough** — forward the validated inbound token unchanged (see [Outbound Authentication](/docs/ai-gateway/mcp/mcp-gateway-auth-security#outbound-authentication)). A last resort, only when the downstream server validates the same IdP and you accept the broader scope.

    <Warning>
      The MCP authorization spec **prohibits token passthrough**: an MCP server must reject any token not minted for its exact audience, precisely to stop a server being used as an open proxy into other systems. Wherever the target enforces this (any OAuth 2.1-compliant MCP server), use a scoped exchanged token — not passthrough.
    </Warning>
  </Accordion>
</AccordionGroup>

## The MCP Gateway with agent identity

The [MCP Gateway](/docs/ai-gateway/mcp/mcp-overview) already separates [inbound authentication](/docs/ai-gateway/mcp/mcp-gateway-auth-security#inbound-authentication) (who is calling the Gateway) from [outbound authentication](/docs/ai-gateway/mcp/mcp-gateway-auth-security#outbound-authentication) (how the Gateway authenticates to the MCP server). Agent identity extends the inbound side: the caller can now be an **agent**, a **user**, or **both at once**, and access control reasons about all of them.

### What you configure per MCP server

On each MCP server you grant access to two kinds of principals, and you can scope at the tool level.

| Setting                                      | Description                                                                                                                                                                |
| -------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Which agents can access this server**      | The agent identities permitted to call this MCP server.                                                                                                                    |
| **Which users/teams can access this server** | The users, teams, or virtual accounts permitted (the existing collaborators model).                                                                                        |
| **Tool-level scope**                         | Restrict an agent (or user) to a subset of tools — for example, an agent gets only 3 of the server's 10 tools. Enforced by the Gateway before the call reaches the server. |

<Tip>
  Tool-level scoping is also how a [Virtual MCP Server](/docs/ai-gateway/mcp/virtual-mcp-server) works — you can curate a tool subset and grant agents access to that instead of the full server.
</Tip>

### How the Gateway decides

When a request reaches an MCP server, the Gateway resolves whatever identities are present, then authorizes in a fixed order.

```mermaid theme={"dark"}
flowchart TD
    Start([Request hits MCP Gateway]) --> Resolve[Resolve identities on the request]
    Resolve --> HasAgent{Agent identity present?}

    HasAgent -->|Yes| AgentAllowed{Agent allowed to access this MCP server?}
    AgentAllowed -->|No| Deny[Deny: 403]
    AgentAllowed -->|Yes| HasUserWithAgent{User identity also present?}

    HasUserWithAgent -->|Yes| MayAct{Agent allowed to act on behalf of this user?}
    MayAct -->|No| Deny
    MayAct -->|Yes| ScopeDeleg[Exchange to a delegated token: user as subject, agent as actor, scoped to MCP server + allowed tools]

    HasUserWithAgent -->|No| ScopeAgent[Exchange to a token for the agent, scoped to MCP server + allowed tools]

    HasAgent -->|No| HasUserOnly{User identity present?}
    HasUserOnly -->|No| Deny
    HasUserOnly -->|Yes| UserAllowed{User allowed to access this MCP server?}
    UserAllowed -->|No| Deny
    UserAllowed -->|Yes| ScopeUser[Scope token to MCP server + allowed tools]

    ScopeDeleg --> Forward[Forward scoped token to MCP server]
    ScopeAgent --> Forward
    ScopeUser --> Forward
    Forward --> Done([MCP server enforces its own policy on the scoped token])
```

The key checks, in plain terms:

1. **If an agent identity is present**, the Gateway first checks the agent is allowed to access this MCP server. If a user identity is also present, it additionally checks the agent is allowed to **act on behalf of that user**. Only then does it mint a delegated token (user as subject, agent as actor) **scoped down** to this MCP server and the allowed tools.
2. **If only a user identity is present** (some agents today have no way to pass an agent identity), the Gateway checks whether the user can access the MCP server and scopes the token accordingly. This keeps existing per-user flows working and gives a clean upgrade path to add agent identity later.
3. **Tool scope** is applied as part of minting the token, so the MCP server only ever receives a token good for the tools the caller is allowed to use.

### Runtime flow

The sequence below shows an agent calling an MCP server on behalf of a user, with the inbound IdP token exchanged for a scoped downstream token.

```mermaid theme={"dark"}
sequenceDiagram
    participant Agent as Agent (presents agent identity + user identity)
    participant GW as MCP Gateway
    participant IdP as IdP / STS
    participant MCP as MCP Server

    Agent->>GW: Call tool (agent identity token + user identity)
    GW->>GW: Validate agent identity
    GW->>GW: Agent allowed on this MCP server?
    GW->>GW: Agent may act on behalf of this user?
    GW->>GW: Is the tool within the agent's allowed scope?
    GW->>IdP: Token exchange (subject = user, actor = agent, audience = MCP server, scope = allowed tools)
    IdP-->>GW: Scoped delegated token (composite: user + act:agent)
    GW->>MCP: Forward request with scoped token (outbound auth)
    MCP->>MCP: Validate token, enforce local policy
    MCP-->>GW: Result
    GW-->>Agent: Result
```

<Note>
  The outbound step reuses the existing [outbound authentication](/docs/ai-gateway/mcp/mcp-gateway-auth-security#outbound-authentication) models. Token exchange (ID-JAG / OBO / TrueFoundry-issued scoped token) is the path used when the MCP server expects a delegated token. Token passthrough, OAuth2, and static credentials remain available for servers that need them.
</Note>

## The Agent Gateway

The Agent Gateway is to agents what the MCP Gateway is to tools: a proxy layer that governs calls **into** registered agents, using the [agent registration](#agent-registration) you defined earlier. One difference shapes the whole design.

<Warning>
  Not every agent can be proxied. Agents embedded inside third-party software (a copilot inside a SaaS product, for example) expose no callable URL — you can only reach them through that product's own interface. For those, TrueFoundry governs their **outbound** calls to MCP servers (via the MCP Gateway), not the inbound call to the agent. The Agent Gateway governs agents that **do** expose a URL: ones you build yourself, or remote agents you register.
</Warning>

### How the Gateway decides

When a request hits the Agent Gateway, it first derives the **caller entity**, then authorizes based on what the caller is.

```mermaid theme={"dark"}
flowchart TD
    Start([Request hits Agent Gateway]) --> Derive[Derive caller entity]
    Derive --> Type{Caller type?}

    Type -->|User or Virtual Account| UserAllowed{Allowed to call this agent?}
    UserAllowed -->|No| Deny[Deny: 403]
    UserAllowed -->|Yes| PassUser[Forward with the caller's identity]

    Type -->|Agent| ValidateAgent[Validate token, resolve agent identity]
    ValidateAgent --> AgentAllowed{Caller agent allowed to call this agent?}
    AgentAllowed -->|No| Deny
    AgentAllowed -->|Yes| HasUser{User identity present? OBO or separate}
    HasUser -->|No| ScopeAgentOnly[Exchange to token scoped to callee agent, actor = caller agent]
    HasUser -->|Yes| MayAct{Caller agent may act on behalf of this user?}
    MayAct -->|No| Deny
    MayAct -->|Yes| ScopeDeleg[Exchange to delegated token: user subject, caller agent actor, audience = callee agent]

    PassUser --> Forward[Forward to downstream agent URL]
    ScopeAgentOnly --> Forward
    ScopeDeleg --> Forward
    Forward --> Downstream([Downstream agent attaches its own identity and carries the user identity forward])
```

In plain terms:

1. **Derive the caller.** A user or virtual account is authorized directly against the callee agent's "who can call me" list, and its identity is forwarded.
2. **If the caller is another agent**, validate its token and resolve the agent identity. Check the caller agent is allowed to call this agent.
3. **If a user identity is also present** — either as an OBO/delegated token or as a separate user identity alongside the agent identity — check the caller agent **may act on behalf of that user**. If yes, the Gateway exchanges the token to a delegated token audienced to the callee agent: user as subject, caller agent as actor, scope narrowed.
4. **The downstream agent then continues the chain.** It attaches its **own** agent identity for the next hop while carrying the user identity forward, so the original user remains the subject all the way down.

### Runtime flow

```mermaid theme={"dark"}
sequenceDiagram
    participant Caller as Caller Agent (on behalf of user)
    participant AGW as Agent Gateway
    participant IdP as IdP / STS
    participant Callee as Callee Agent

    Caller->>AGW: Invoke callee agent (caller agent identity + user identity)
    AGW->>AGW: Resolve caller = agent identity
    AGW->>AGW: Caller agent allowed to call callee?
    AGW->>AGW: Caller agent may act on behalf of user?
    AGW->>IdP: Token exchange (subject = user, actor = caller agent, audience = callee agent)
    IdP-->>AGW: Delegated token audienced to callee agent
    AGW->>Callee: Forward request with scoped token
    Callee->>Callee: Now becomes a caller for its own downstream hops
    Callee-->>AGW: Result
    AGW-->>Caller: Result
```

## Policies at both gateways

The allow-lists on registrations (who can call, whom to act for, which tools) cover most needs. For finer-grained, attribute-based rules, both gateways can evaluate a **policy language** on every request — before any token is minted or forwarded. This is where conditions like *"only during business hours"* or *"only for low-risk tools"* live, and it's the same engine at the MCP Gateway and the Agent Gateway.

[Cedar](https://www.cedarpolicy.com/) is the primary supported policy language. Each request is expressed as a **principal, action, resource, context** (PARC) tuple that maps cleanly onto the identities this page already resolves:

| Cedar element | What it carries at the gateway                                                                           |
| ------------- | -------------------------------------------------------------------------------------------------------- |
| **principal** | The caller — an agent identity, user, or virtual account.                                                |
| **action**    | The operation — `mcp:callTool`, `agent:invoke`, etc.                                                     |
| **resource**  | The callee — an MCP server, a specific tool, or an agent.                                                |
| **context**   | Everything else — the user being acted for, the actor chain, time, request risk, source IP, environment. |

Cedar is **default-deny**, and `forbid` always overrides `permit`, so guardrails are easy to express. A few examples of what becomes possible:

```cedar theme={"dark"}
// The support copilot may only READ Jira, even when acting for any user.
permit (
  principal == AgentIdentity::"support-copilot",
  action == Action::"mcp:callTool",
  resource == Tool::"jira/issues.read"
);

// Any agent may write to Jira only on behalf of the engineering team,
// and only during business hours.
permit (
  principal,
  action == Action::"mcp:callTool",
  resource == Tool::"jira/issues.write"
)
when {
  context.on_behalf_of in Team::"engineering" &&
  context.time.hour >= 9 && context.time.hour < 18
};

// Block destructive tools for any agent more than two hops deep in a chain.
forbid (
  principal,
  action == Action::"mcp:callTool",
  resource in ToolGroup::"destructive"
)
when { context.actor_chain.length > 2 };
```

The first two policies show the **(agent, user, tool)** differentiation called out in [the governance problems](#the-governance-problems): the same user driving different agents gets different reach. The third shows a chain-depth guardrail that an allow-list alone can't express.

<Note>
  Policy evaluation composes with, and runs alongside, the existing [MCP guardrails](/docs/ai-gateway/guardrails-overview) (pre- and post-tool checks for content, PII, and the like). Cedar decides **whether** a call is allowed; guardrails inspect the **content** of the request and response. If your organization standardizes on a different engine such as [OPA/Rego](https://www.openpolicyagent.org/), the same PARC inputs apply.
</Note>

## End-to-end: agent → agent → MCP

This is where the model pays off. A planner agent, acting for user Jane, calls a research agent, which calls a Jira MCP server. Across the chain the subject stays Jane, the actor chain grows, and the audience and scope narrow to exactly the next callee.

How the *entry* agent (the planner) is reached depends on whether it exposes a proxyable URL — so there are two cases. The downstream hops are governed identically in both; only the first hop differs.

<Frame caption="The token across the chain: the user stays the subject at every hop, the actor chain grows, and the audience and scope narrow to the next callee">
  <img src="https://mintcdn.com/truefoundry/I_15w3VXjxbEnwEy/images/agent_token_flow_end_to_end.png?fit=max&auto=format&n=I_15w3VXjxbEnwEy&q=85&s=58adcee67785817af82a4d3292e76d4b" alt="End-to-end diagram: User Jane to Agent Gateway to Planner Agent to Agent Gateway to Research Agent to MCP Gateway to Jira MCP. Token cards above each hop show user Jane preserved throughout, with actor and audience claims added and scope narrowing to read by the final hop" width="1536" height="1024" data-path="images/agent_token_flow_end_to_end.png" />
</Frame>

### Case A — the entry agent is proxied

The planner exposes a URL and is registered on the Agent Gateway, so even Jane's first call is proxied and governed. Every hop flows through a gateway.

```mermaid theme={"dark"}
sequenceDiagram
    participant Jane as User (Jane)
    participant AGW as Agent Gateway
    participant Planner as Planner Agent
    participant Research as Research Agent
    participant MGW as MCP Gateway
    participant Jira as Jira MCP Server

    Jane->>AGW: "Summarize my open Jira issues" (Jane's identity)
    AGW->>AGW: Jane allowed to call Planner?
    AGW->>Planner: Forward as Jane
    Planner->>AGW: Call Research Agent (sub=Jane, actor=Planner)
    AGW->>AGW: Planner may call Research? may act for Jane?
    AGW->>Research: Token audienced to Research<br/>sub=Jane, act=Planner
    Research->>MGW: Call Jira tool (sub=Jane, actor=Research)
    MGW->>MGW: Research allowed on Jira MCP? may act for Jane?<br/>tool in scope?
    MGW->>Jira: Token audienced to Jira, scope=issues.read<br/>sub=Jane, act=[Research, Planner]
    Jira-->>MGW: Jane's open issues
    MGW-->>Research: Result
    Research-->>AGW: Summary
    AGW-->>Planner: Summary
    Planner-->>AGW: Summary
    AGW-->>Jane: "You have 4 open issues..."
```

### Case B — the entry agent is embedded (not proxyable)

The planner is a copilot inside a third-party product (or the user's IDE) with no callable URL. Jane interacts with it through that product's own interface, so the first hop **cannot** be proxied. The planner still presents its registered agent identity on its outbound calls, so every hop *it* makes downstream is governed exactly as in Case A.

```mermaid theme={"dark"}
sequenceDiagram
    participant Jane as User (Jane)
    participant Planner as Planner Agent (embedded, no URL)
    participant AGW as Agent Gateway
    participant Research as Research Agent
    participant MGW as MCP Gateway
    participant Jira as Jira MCP Server

    Jane->>Planner: "Summarize my open Jira issues" (via the product's own UI)
    Note over Planner: Not proxyable — but presents its registered<br/>agent identity + Jane's identity downstream
    Planner->>AGW: Call Research Agent (sub=Jane, actor=Planner)
    AGW->>AGW: Planner may call Research? may act for Jane?
    AGW->>Research: Token audienced to Research<br/>sub=Jane, act=Planner
    Research->>MGW: Call Jira tool (sub=Jane, actor=Research)
    MGW->>MGW: Research allowed on Jira MCP? may act for Jane?<br/>tool in scope?
    MGW->>Jira: Token audienced to Jira, scope=issues.read<br/>sub=Jane, act=[Research, Planner]
    Jira-->>MGW: Jane's open issues
    MGW-->>Research: Result
    Research-->>AGW: Summary
    AGW-->>Planner: Summary
    Planner-->>Jane: "You have 4 open issues..."
```

<Note>
  The only difference between the two cases is the first hop. In Case A the Agent Gateway also governs the inbound call into the planner; in Case B the planner is reached through its host product, and governance starts at the planner's first outbound call. For embedded agents, the planner's `act_on_behalf_of` policy is still enforced — it just comes from a governance-only [agent registration](#agent-registration) rather than a proxied one.
</Note>

How the token transforms along the governed hops (identical in both cases):

| Hop                 | Subject (`sub`) | Actor chain (`act`) | Audience (`aud`) | Scope                |
| ------------------- | --------------- | ------------------- | ---------------- | -------------------- |
| Jane → Planner      | Jane            | —                   | Planner          | (full user context)  |
| Planner → Research  | Jane            | Planner             | Research Agent   | narrowed to Research |
| Research → Jira MCP | Jane            | Research → Planner  | Jira MCP Server  | `issues.read` only   |

Two properties hold at every hop, and together they are the whole point of the design:

* **The user is never lost.** Jira sees that this request is *for Jane*, so it returns only Jane's issues and enforces Jane's permissions.
* **Every actor is recorded and every token is minimal.** The actor chain is a complete audit trail, and no token is ever broader than the single next callee needs.

## Governance and observability

Because every hop is an explicit, authorized exchange, the Gateway has a full record of each one.

<CardGroup cols={2}>
  <Card title="Per-hop attribution" icon="route">
    Every call records the subject (user), the acting agent, and the full actor chain — visible in [traces](/docs/agent-platform/agent-registry/remote-agents#traces).
  </Card>

  <Card title="Least-privilege tokens" icon="key">
    Tokens are scoped to a single callee and a minimal tool/scope set, so a leaked token has limited blast radius.
  </Card>

  <Card title="Central policy" icon="shield">
    "Who can call what" and "who can act for whom" live in one place, enforced at the Gateway before any token is minted.
  </Card>

  <Card title="Lifecycle control" icon="rotate">
    Agent identity tokens support expiry, auto-rotation, notifications, and secret-manager sync from the registration form.
  </Card>
</CardGroup>

## Portable trust across clouds

Minting an agent identity is no longer the hard part — every major platform can do it. Microsoft Entra Agent ID roots agents in Entra tenant objects, AWS Bedrock AgentCore issues first-party workload identities, and Google Cloud issues SPIFFE-based principals. The genuinely unsolved industry problem is **portable trust**: each hyperscaler's trust model stops at its own perimeter. An AWS workload token isn't valid against a Google resource, and dropping a static cross-cloud key back into an agent throws away the secretless posture that made the model safe in the first place.

This is exactly the gap a neutral gateway closes. Because TrueFoundry sits in front of MCP servers and agents rather than inside any one cloud's identity plane, it can act as the broker that:

* accepts whatever identity each agent already has — an Entra agent identity, an AWS workload identity, a Google/SPIFFE principal, or a TrueFoundry-issued token;
* validates it through a configured [Identity Provider](/docs/platform/identity-providers) and resolves it to a single agent identity;
* performs the [token exchange](#token-exchange-the-mechanism-that-carries-identity) to mint a token the *next* callee trusts, preserving `sub` and the `act` chain across the trust-domain boundary; and
* keeps the policy and audit schema in one place, independent of any single vendor's logs — so your trust anchor survives a cloud migration.

<Tip>
  The practical pattern is a "portable trust sandwich": vendor-neutral workload identity (SPIFFE or OIDC federation) at the bottom, RFC 8693 token exchange preserving subject and actor in the middle, sender-constrained tokens on top, and a gateway minting just-in-time downstream credentials wherever a system can't consume those semantics directly.
</Tip>

## Putting it together

| You want to...                                          | Use                                                                                                                                                     |
| ------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Give an agent a verifiable identity                     | Register an [Agent Identity](#agent-identity) (TrueFoundry-backed or IdP-backed).                                                                       |
| Let an agent call MCP servers for a user                | Grant the agent access on the MCP server, allow it to act for the user, and let the Gateway [exchange the token](#the-mcp-gateway-with-agent-identity). |
| Restrict an agent to specific tools                     | Apply [tool-level scope](#what-you-configure-per-mcp-server) on the MCP server.                                                                         |
| Let one agent call another                              | Register the callee on the [Agent Gateway](#the-agent-gateway) and add the caller agent to its "who can call me" list.                                  |
| Carry the user across many hops                         | Rely on [delegation](#delegation-vs-impersonation) — the subject stays the user, the actor chain grows.                                                 |
| Express attribute-based rules (time, risk, chain depth) | Write [Cedar policies](#policies-at-both-gateways) evaluated at both gateways.                                                                          |
| Integrate with Okta or Entra                            | Use an [IdP-backed identity](#how-an-agent-gets-its-identity); TrueFoundry uses [ID-JAG or OBO](#how-this-maps-to-your-idp) accordingly.                |

## Standards this builds on

The model is an assembly of open standards rather than a proprietary scheme, which is what lets it span IdPs, clouds, and frameworks.

| Standard                                                                                                                                       | Role here                                                                                              |
| ---------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ |
| [OAuth 2.0 Token Exchange (RFC 8693)](https://www.rfc-editor.org/rfc/rfc8693.html)                                                             | The core delegation mechanism — `subject_token`, `actor_token`, and the `act` / `may_act` claims.      |
| [JWT Profile for OAuth (RFC 7523)](https://www.rfc-editor.org/rfc/rfc7523.html)                                                                | JWT bearer grant used to redeem an ID-JAG at the resource's authorization server.                      |
| [Identity Assertion JWT Authorization Grant (ID-JAG / XAA)](https://datatracker.ietf.org/doc/draft-ietf-oauth-identity-assertion-authz-grant/) | Okta's cross-app access profile for brokering access through the enterprise IdP.                       |
| [Entra On-Behalf-Of](https://learn.microsoft.com/en-us/entra/agent-id/agent-on-behalf-of-oauth-flow)                                           | Microsoft's agent-aware OBO profile of token exchange.                                                 |
| [SPIFFE / SPIRE](https://spiffe.io/)                                                                                                           | Attested workload identity (X.509-SVID, JWT-SVID) for deriving an agent's credential from its runtime. |
| [WIMSE / WIT-SVID](https://datatracker.ietf.org/doc/draft-ietf-wimse-s2s-protocol/)                                                            | Key-bound workload tokens that resist replay through intermediaries.                                   |
| [SPICE actor chain](https://datatracker.ietf.org/doc/draft-mw-spice-actor-chain/)                                                              | Emerging draft for cryptographically verifiable actor chains across domains.                           |
| [mTLS (RFC 8705)](https://www.rfc-editor.org/rfc/rfc8705.html) · [DPoP (RFC 9449)](https://www.rfc-editor.org/rfc/rfc9449.html)                | Sender-constrained tokens so a stolen token can't be replayed.                                         |
| [A2A](https://google.github.io/A2A/)                                                                                                           | Agent-to-agent transport, discovery, and signed agent cards.                                           |
| [Cedar](https://www.cedarpolicy.com/)                                                                                                          | Attribute-based authorization policy evaluated at both gateways.                                       |

## FAQ

<AccordionGroup>
  <Accordion title="What if an agent can't pass an agent identity yet?">
    If a user identity is present, the Gateway can treat the request as a plain user call: as long as the user is allowed to access the MCP server or agent, it proceeds and the token is scoped to the user — with no agent-level attribution. This is a transitional path for agents that can't yet present an identity. Once the agent starts presenting a registered agent identity, the agent-level checks and actor attribution turn on automatically, with no change to the downstream server. You can require an agent identity (and disable the user-only fallback) where you want strict enforcement.
  </Accordion>

  <Accordion title="Delegation or impersonation — which does TrueFoundry use?">
    Delegation, by default. The downstream service always sees both the user (as subject) and the acting agent (in the `act` claim), so every call is attributable. Impersonation hides the agent and is avoided unless a downstream server genuinely cannot understand a composite token.
  </Accordion>

  <Accordion title="Do I need Okta or Entra to use this?">
    No. With a TrueFoundry-backed agent identity, the Gateway acts as the Security Token Service and mints scoped, delegated tokens itself. Okta (ID-JAG) and Entra (OBO) are used when you want your IdP to broker cross-application access centrally.
  </Accordion>

  <Accordion title="What's the difference between this and token passthrough?">
    [Token passthrough](/docs/ai-gateway/mcp/mcp-gateway-auth-security#outbound-authentication) forwards the same token unchanged — simple, but the token isn't scoped down and carries no actor chain. Token exchange mints a *new* token per hop, scoped to the next callee and recording the acting agent. The MCP authorization spec actually prohibits passthrough (a server must reject tokens not minted for its exact audience), so prefer exchange everywhere; reserve passthrough for trusted internal servers that validate the same IdP and don't enforce audience.
  </Accordion>

  <Accordion title="How are agents embedded in third-party software governed?">
    You usually can't proxy the inbound call to them, since they have no callable URL. Instead, govern their **outbound** calls to MCP servers through the [MCP Gateway](/docs/ai-gateway/mcp/mcp-overview). The agent presents its agent identity (and the user identity, if available) to the MCP Gateway, and all the access checks and scoping in this page apply.
  </Accordion>
</AccordionGroup>
