> ## 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 Governance with Okta

> Governing agents when identities live in Okta — Cross App Access (ID-JAG), On-Behalf-Of token exchange, the Okta for AI Agents product, and how the TrueFoundry gateway drives each flow.

Use this scenario when your enterprise is standardized on Okta: users sign in through Okta, applications are registered as Okta apps, and — optionally — agents are registered as first-class identities through the **Okta for AI Agents** product. TrueFoundry validates Okta-issued tokens on the inbound side (an [identity provider-backed agent identity](/docs/ai-gateway/agents/agent-identity#where-an-identity-comes-from)) and drives the Okta token exchanges on the outbound side, so no agent or app author implements an exchange themselves.

<Warning>
  Two status facts frame everything, because they are the most misunderstood:

  * **Cross App Access (XAA)** — Okta's productization of ID-JAG — is **Early Access** as of mid-2026 (enablement is not self-service; GA targeted for FY27).
  * **[Okta for AI Agents](https://www.okta.com/products/govern-ai-agent-identity/)** is a **separate, paid product** (GA April 2026). It is *not* the same thing as XAA. The distinction between "the protocol" (XAA/ID-JAG, usable on base Okta) and "the governance layer" (the paid product) is the crux of this page.
</Warning>

## The building blocks Okta gives you

| Building block                                                                           | Role in the model                                                                                           | Notes                                                                                                                                                                |
| ---------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Okta as IdP**                                                                          | Authenticates the user, issues the ID token / SAML assertion, mints the **ID-JAG**.                         | The identity-assertion side of every flow.                                                                                                                           |
| **Custom authorization server**                                                          | Redeems tokens and mints resource-scoped access tokens.                                                     | **OBO** (RFC 8693) runs only here — the Org AS can't (no custom scopes/claims/audience). Requires the **API Access Management** SKU.                                 |
| **[XAA / ID-JAG](https://www.okta.com/solutions/cross-app-access/)**                     | Cross-app / cross-domain access brokered by Okta.                                                           | Early Access. The two-legged exchange described in [key concepts](/docs/agent-platform/agent-governance/key-concepts#across-trust-domains-id-jag--cross-app-access). |
| **[OBO (RFC 8693)](https://developer.okta.com/docs/guides/set-up-token-exchange/main/)** | Same-tenant token exchange between custom authorization servers.                                            | GA. Carries the user forward but emits **no `act` claim** — no actor chain.                                                                                          |
| **[Okta for AI Agents](https://www.okta.com/products/govern-ai-agent-identity/)**        | Agent registry in Universal Directory, shadow-agent discovery, secret vaulting, kill switch, certification. | Paid product. Also the *licensing gate* for agent-to-agent token exchange.                                                                                           |
| **OIN (Okta Integration Network)**                                                       | The catalog both apps must be listed in for XAA, with an app role of Requesting / Resource / Both.          | The "Both" role lets an intermediary like a gateway sit in a chain.                                                                                                  |

### Okta application vs. Okta AI agent

The patterns below lean on two different kinds of principal:

* **Okta application** — an app registered in Okta as an OIDC/OAuth client and (for XAA) listed in the OIN. Base Workforce Identity functionality; in an XAA exchange it plays the *requesting* or *resource* role.
* **Okta AI agent** — an autonomous agent registered as a **first-class non-human identity** in Universal Directory through the paid Okta for AI Agents product. Beyond a plain application it gets an **owner**, a managed lifecycle (onboard, certify, kill switch), optional secret vaulting, and the entitlement to perform **agent-to-agent token exchange** — the onward, per-hop ID-JAG minting that a multi-agent chain needs.

## Which pattern applies

Match your call path against this table; each pattern is detailed below. "3P" means a third party outside your Okta trust.

| Call path                                      | Mechanism                                | Needs Okta for AI Agents? |
| ---------------------------------------------- | ---------------------------------------- | ------------------------- |
| Okta app → MCP server (Okta custom AS)         | XAA / ID-JAG                             | No                        |
| Okta app → 3P MCP server whose AS trusts Okta  | XAA / ID-JAG                             | No                        |
| Okta app → 3P MCP server, no Okta trust        | Per-user OAuth (managed by the gateway)  | No                        |
| Okta app → agent → agent → MCP server          | Chained ID-JAG (agent-to-agent exchange) | **Yes**                   |
| Okta app → Okta app → MCP server (same tenant) | OBO — user preserved, **no actor chain** | No                        |

### Pattern 1 — Okta app → MCP server, via XAA

Canonical Cross App Access. The MCP Gateway mints an ID-JAG at Okta and redeems it at the MCP's authorization server for a scoped access token.

```mermaid theme={"dark"}
sequenceDiagram
    participant C as Client App
    participant MGW as TFY MCP Gateway
    participant Okta as Okta IdP
    participant RAS as MCP Authorization Server
    participant MCP as MCP Server
    C->>MGW: Call tool with Okta ID token
    MGW->>Okta: Leg 1 token-exchange, requested=id-jag, aud=MCP authz
    Okta-->>MGW: ID-JAG
    MGW->>RAS: Leg 2 jwt-bearer, assertion=ID-JAG
    RAS-->>MGW: Scoped access token
    MGW->>MCP: Call tool with scoped token
    MCP-->>MGW: Result
    MGW-->>C: Result
```

<AccordionGroup>
  <Accordion title="Okta prerequisites" icon="key">
    * Register the **client application** as an Okta OIDC app (the *requesting* app), so a signed-in user obtains an Okta ID token.
    * Register the **MCP server** as an Okta app fronted by a **custom authorization server** (the *resource* app) — the server that redeems the ID-JAG.
    * Establish the **Cross App Access trust**: in the Okta Admin Console, create a managed connection from the requesting app to the resource app. (XAA is Early Access — enablement goes through Okta support, and both apps must be OIN-listed.)
  </Accordion>

  <Accordion title="TrueFoundry setup" icon="gear">
    <Steps>
      <Step title="Configure the MCP server's Auth Data for Cross App Access">
        On the MCP server, set **Auth Type = OAuth2**, **OAuth Provider = Okta**, **Grant Type = ID-JAG (XAA – Cross App Access)**, then fill **Resource Token URL** (the resource AS token endpoint where the ID-JAG is redeemed), **Resource Client ID / Secret** (the client the gateway authenticates the redemption with), and **Issuer URI** (the resource AS issuer — the ID-JAG `audience`).

        <Frame caption="MCP server outbound Auth Data configured for Okta ID-JAG (Cross App Access)">
          <img src="https://mintcdn.com/truefoundry/ObbMHkc6Id9XfbZx/images/agent-registry/pattern1-mcp-xaa-auth-data.png?fit=max&auto=format&n=ObbMHkc6Id9XfbZx&q=85&s=d45f1c12624b3bccb86b0c2dafdd5e45" alt="TrueFoundry MCP server Auth Data form: Auth Type OAuth2, OAuth Provider Okta, Grant Type ID-JAG (XAA - Cross App Access), with Resource Token URL, Resource Client ID, Resource Client Secret, and Issuer URI fields" width="1312" height="1456" data-path="images/agent-registry/pattern1-mcp-xaa-auth-data.png" />
        </Frame>
      </Step>

      <Step title="Create a virtual account for the client application">
        Create a [virtual account](/docs/platform/virtual-account-management) representing the client app and grant it **MCP Server User** on the target server. At runtime the client presents this alongside the user's Okta ID token, and the gateway runs the exchange above.

        <Frame caption="Virtual account representing the client app, granted MCP Server User on the target MCP server">
          <img src="https://mintcdn.com/truefoundry/ObbMHkc6Id9XfbZx/images/agent-registry/pattern1-client-va-mcp-permission.png?fit=max&auto=format&n=ObbMHkc6Id9XfbZx&q=85&s=7667315e169468b0ba8265613fe9b13d" alt="TrueFoundry virtual account MCP Servers permission: slack-xaa with role MCP Server User" width="2066" height="350" data-path="images/agent-registry/pattern1-client-va-mcp-permission.png" />
        </Frame>
      </Step>
    </Steps>
  </Accordion>
</AccordionGroup>

### Pattern 2 — third-party MCP whose authorization server trusts Okta

The cross-vendor case, and the reason the IdP and authorization server are separate roles: the AS that redeems the ID-JAG does **not** have to be run by Okta. It just has to **trust** Okta as an ID-JAG issuer — a small explicit configuration on the resource side (Okta's issuer identifier, its JWKS to verify signatures, and the accepted audience). The resource domain then validates ID-JAGs locally and mints its *own* tokens under its *own* policy — it never imports your user database and keeps full control of scopes and revocation.

```mermaid theme={"dark"}
flowchart LR
    subgraph DA[Requesting domain]
      App[Requesting app or agent]
      Okta[(Okta IdP)]
    end
    subgraph DB[Resource domain - different vendor]
      RAS[3P Authorization Server<br/>trusts Okta issuer, JWKS, audience]
      MCP[(3P MCP server)]
    end
    App -->|ID token| Okta
    Okta -->|ID-JAG audienced to RAS| App
    App -->|jwt-bearer ID-JAG| RAS
    RAS -->|scoped access token| App
    App -->|Bearer token| MCP
```

**Setup is identical to pattern 1** — the same ID-JAG Auth Data and client virtual account — except **Resource Token URL** and **Issuer URI** point at the third party's authorization server instead of an Okta custom AS.

### Pattern 3 — third-party MCP with no Okta trust: per-user OAuth

When the third party is not part of any XAA trust, the flow degrades to standard 3-legged OAuth, managed by the MCP Gateway per user — consent, token storage, refresh. On the MCP server set **Auth Data → OAuth2**, provider **Custom**, grant **Authorization Code**; each user authorizes their own account on first use (the gateway returns a `401` with an authorization URL). No Okta XAA setup is involved.

<Frame caption="MCP server outbound Auth Data for standard per-user OAuth (Authorization Code)">
  <img src="https://mintcdn.com/truefoundry/ObbMHkc6Id9XfbZx/images/agent-registry/pattern3-mcp-oauth-authcode.png?fit=max&auto=format&n=ObbMHkc6Id9XfbZx&q=85&s=04aa1be0d38ca333cae86027b45da1de" alt="TrueFoundry MCP server Auth Data form: Auth Type OAuth2, OAuth Provider Custom, Grant Type Authorization Code, with Authorization URL, Token URL, Client ID, and Client Secret fields" width="1090" height="1200" data-path="images/agent-registry/pattern3-mcp-oauth-authcode.png" />
</Frame>

<Note>
  At this boundary you lose the propagated identity assertion, the admin-visible connection, and centralized revocation — end-to-end governance stops at the untrusted hop. The gateway's own audit and access control still apply.
</Note>

### Pattern 4 — multi-agent chains: Okta AI agents and chained ID-JAG

The full agentic call path — app → agent → agent → MCP — built on Okta's [agent-to-agent token exchange](https://developer.okta.com/docs/guides/ai-agent-to-agent-token-exchange/agent-to-agent/main/). Each hop mints a **fresh ID-JAG** audienced to the next callee's authorization server and nests the acting agent into the `act` chain. The first leg starts from the user's ID token; **onward legs are seeded by the previous hop's access token** — the paid AI Agents product extends the base ID-JAG rule (which forbids an access-token subject) to allow exactly this. It is a *chain of exchanges*, not one carried token.

```mermaid theme={"dark"}
sequenceDiagram
    participant C as Client App
    participant AGW as TFY Agent Gateway
    participant A1 as Agent A
    participant A2 as Agent B
    participant MGW as TFY MCP Gateway
    participant Okta as Okta
    participant MCP as MCP Server
    C->>AGW: Invoke Agent A with Okta ID token
    AGW->>Okta: token-exchange, subject=ID token, requested=ID-JAG, aud=Agent A AS
    Okta-->>AGW: Fresh ID-JAG, redeemed to access token
    AGW->>A1: Forward, sub=user
    A1->>AGW: Invoke Agent B
    AGW->>Okta: token-exchange, subject=prev access token, requested=ID-JAG, aud=Agent B AS
    Okta-->>AGW: Fresh ID-JAG, act chain nests Agent A
    AGW->>A2: Forward, sub=user act=Agent A
    A2->>MGW: Call MCP tool
    MGW->>Okta: token-exchange, requested=ID-JAG, aud=MCP AS
    Okta-->>MGW: Fresh ID-JAG, redeemed to scoped token
    MGW->>MCP: Call tool with scoped token
    MCP-->>MGW: Result
    MGW-->>A2: Result
    A2-->>AGW: Result
    AGW-->>C: Result
```

<AccordionGroup>
  <Accordion title="Okta setup" icon="key">
    <Steps>
      <Step title="Register each agent as an Okta AI agent">
        In the Okta Admin Console (**Okta for AI Agents**), add each agent to Universal Directory with a human **owner**. Each gets a first-class non-human identity backed by an OAuth client. *(Requires the paid product.)*
      </Step>

      <Step title="Give each agent a custom authorization server">
        Its issuer becomes the ID-JAG `audience` for calls *to* that agent. Agents can share one custom AS (distinct scopes) or use one each.
      </Step>

      <Step title="Register the MCP server as a resource">
        The terminal MCP server is a resource protected by its custom authorization server, which redeems the final ID-JAG.
      </Step>

      <Step title="Authorize the connections along the chain">
        Configure connections for each hop — client → Agent A, Agent A → Agent B, Agent B → MCP — so Okta may issue a fresh ID-JAG per hop.
      </Step>

      <Step title="Share credentials with TrueFoundry">
        Provide each agent's client ID and signing key plus AS details, so the gateways can perform the exchanges on the chain's behalf.
      </Step>
    </Steps>
  </Accordion>

  <Accordion title="TrueFoundry setup" icon="gear">
    On **each agent** and on the **MCP server**, set **Auth Data → OAuth2**, **Grant Type = ID-JAG (Okta AI Agent)**, filling **Resource Token URL** (the next callee's AS token endpoint), **Resource Client ID**, **Private Key** (Okta AI agents authenticate with a key, not a shared secret), and **Issuer URI** (the callee's AS issuer). The Agent Gateway then mints the fresh ID-JAG at each agent hop and the MCP Gateway performs the final one.

    <Frame caption="Agent outbound Auth Data configured for ID-JAG (Okta AI Agent)">
      <img src="https://mintcdn.com/truefoundry/ObbMHkc6Id9XfbZx/images/agent-registry/pattern4-agent-idjag-auth-data.png?fit=max&auto=format&n=ObbMHkc6Id9XfbZx&q=85&s=46a9e12db2ccd239f13ba26d4726a8de" alt="TrueFoundry Auth Data form: Auth Type OAuth2, OAuth Provider Custom, Grant Type ID-JAG (Okta AI Agent), with Resource Token URL, Resource Client ID, Private Key, and Issuer URI fields" width="822" height="1192" data-path="images/agent-registry/pattern4-agent-idjag-auth-data.png" />
    </Frame>

    If the chain terminates at a **third-party** resource instead, Okta brokers a third-party access token for the last hop ("Resource server" connected-resource type) — register that terminal MCP server with **OBO (Token Exchange)** Auth Data instead of ID-JAG.
  </Accordion>
</AccordionGroup>

### Pattern 5 — same-tenant app-to-app: OBO, without an actor chain

Within one Okta tenant, the gateway performs an RFC 8693 OBO exchange on a custom authorization server. The resulting token keeps the user as `sub` — but Okta's OBO emits **no `act` claim**, so the resource learns *who the user is* but not *which agents touched the request*.

On the MCP server set **Auth Data → OAuth2**, **OAuth Provider = Okta**, **Grant Type = Token Exchange**, with the custom AS **Token URL**, optional **Client ID / Secret**, and **Audience**. No Cross App Access managed connection is needed for OBO.

<Frame caption="MCP server outbound Auth Data configured for On-Behalf-Of (Token Exchange)">
  <img src="https://mintcdn.com/truefoundry/ObbMHkc6Id9XfbZx/images/agent-registry/pattern6-mcp-obo-token-exchange.png?fit=max&auto=format&n=ObbMHkc6Id9XfbZx&q=85&s=dac4a692355083d2f5addb1ada756643" alt="TrueFoundry MCP server Auth Data form: Auth Type OAuth2, Grant Type Token Exchange (on-behalf-of), with Token URL, Client ID, Client Secret, and Audience fields" width="1072" height="1216" data-path="images/agent-registry/pattern6-mcp-obo-token-exchange.png" />
</Frame>

## What the gateway adds on top of Okta

Base XAA describes a *single* requesting-app → resource-app relationship. The TrueFoundry gateways fill three gaps:

* **Multi-hop chaining.** The gateway registers in the OIN with the **"Both"** role — resource app for the upstream hop, requesting app for the downstream hop — and mints a fresh, freshly-audienced ID-JAG per hop, stitching single grants into one governed path.
* **Uniform governance across hops.** Each resource AS enforces only *its* hop; the gateway runs the same [authorize → exchange → guard → record](/docs/agent-platform/agent-governance/governance-blueprint#4-authorize-and-scope-the-per-hop-loop) loop at *every* hop.
* **Abstraction.** Apps and agents present the identity they already have; the gateway does XAA or OBO on their behalf. No app author writes a two-legged exchange — which matters at scale, when many agents talk to each other in a mesh.

## Without the Okta for AI Agents product

Patterns 1–3 and 5 work on base Okta (plus API Access Management for custom authorization servers). What the paid product adds is the *lifecycle layer* (registry, discovery, vaulting, kill switch, certification) and the *agent-to-agent exchange* that pattern 4 needs. The most consequential gap on base Okta: OBO emits no `act` claim, so an app → app → MCP path has **no cryptographic actor chain**.

<Accordion title="Getting a verifiable actor chain without the paid product" icon="wrench">
  Front the resource with an authorization server that *does* support nested actor tokens — for example [Keycloak](https://www.keycloak.org/) — and let the gateway drive it. Keycloak trusts Okta as the IdP (issuer + JWKS + audience), redeems the assertion, and uses its standard token exchange (RFC 8693, GA since Keycloak 26.2) to mint downstream tokens carrying the user as `sub` and the acting agent in `act`, nesting per hop. You get Okta's centralized brokering *and* a verifiable actor chain.

  <Warning>
    Keycloak's standard token exchange is GA, but the full `act`/`may_act` delegation semantics are still behind feature flags in current releases. Validate nested-delegation behavior on your version before depending on it.
  </Warning>
</Accordion>

Note the overlap with TrueFoundry: agent registry, ownership, kill switch, and audit are things the [TrueFoundry implementation](/docs/agent-platform/agent-governance/truefoundry-implementation) provides regardless of Okta licensing. Choose Okta for AI Agents when you want the *directory itself* to be the system of record for agent identities and the per-hop `act` chain minted by Okta.

## FAQ

<AccordionGroup>
  <Accordion title="Must the terminal MCP server be registered as an Okta AI agent?">
    No. The terminal target only needs to be an **XAA-enabled resource app** — OIN-listed, fronted by an AS that redeems the ID-JAG. You register as AI agents the *autonomous actors* you want as first-class identities. An MCP server is a *resource app*, not necessarily an AI agent.
  </Accordion>

  <Accordion title="One shared custom authorization server, or one per app?">
    Multiple apps and agents can share one custom AS (different clients, different scopes). The nuance: each Okta custom AS has a single configured audience, so genuinely different audiences require multiple custom authorization servers or the `audience`/`resource` parameter.
  </Accordion>

  <Accordion title="Must the starting assertion be an Okta-issued ID token?">
    In Okta's XAA, yes — Leg 1 is performed by Okta as the IdP, so the subject token is an Okta-issued ID token (or SAML assertion). Okta can be federated upstream to another IdP, but what is presented to Okta's token endpoint is still the Okta-issued assertion.
  </Accordion>

  <Accordion title="Must every hop be registered in Okta?">
    Every hop that participates in the XAA chain needs the trust relationship — requesting side via OIN/SSO, resource side via an AS that trusts Okta. A plain third-party app with no Okta trust cannot participate; that hop degrades to per-user OAuth (pattern 3).
  </Accordion>
</AccordionGroup>
