Overview
Cross App Access (XAA) lets an administrator approve “Bifrost may reach Linear for our users” once, in Okta. After that, every assigned user’s agent can call the upstream MCP server as that user, with the user’s own permissions there, without connecting anything. It is theid_jag profile of the Token Exchange auth type. For each user, Bifrost:
- Exchanges the user’s Okta ID token or refresh token, captured when they signed in to Bifrost, for an ID-JAG: an identity assertion addressed to the upstream server’s authorization server. Okta applies its Cross App Access policy here.
- Presents the ID-JAG to the upstream authorization server with the
jwt-bearergrant, authenticated as Bifrost’s own client at that server, and receives the user’s access token.
Bifrost Enterprise only, with Okta as the SSO provider: Okta is the identity provider that issues ID-JAGs today. This page covers Bifrost reaching upstream servers. The other direction, AI apps such as Claude reaching Bifrost through Okta, is set up under User Provisioning → Cross App Access.
Prerequisites
- Users sign in to Bifrost through Okta. The subject of the exchange is the token Okta issued to Bifrost’s SSO application, taken from the user’s most recently refreshed SSO session. This works however the request identifies the user, including an identity-provider bearer token or a Bifrost-issued MCP token in user mode. A user with no SSO session gets an error asking them to sign in. Requests that identify no user, such as a plain virtual key, cannot use this profile. The upstream token is cached per user until shortly before it expires.
- Bifrost’s SSO application is registered as an AI agent in Okta. In Okta, go to Directory → AI agents → Register AI agent, and select the existing OIDC application Bifrost uses for SSO. Enable the Token Exchange grant on that application. Okta accepts a subject token only from the application it was issued to, so Bifrost always requests the ID-JAG as this application; there is no separate exchange application to configure. If the agent’s status shows as Staged, choose Actions → Activate before adding a resource connection.
- A resource connection from Bifrost to the service. On the AI agent, add a resource connection to the service’s app in Okta, with the scopes Bifrost may request. Enter the client ID Bifrost has at the service.
- A client for Bifrost at the service’s authorization server. The service must let you register Bifrost as an OAuth client that may use the
jwt-bearergrant. How this is done varies by service, and not every service offers it yet.
Setup
- Web UI
- config.json
- Add the MCP server and choose Token exchange as the auth type.
- Under How the upstream token is obtained, choose Cross App Access (ID-JAG).
- Set Audience to the issuer of the service’s authorization server, as configured in Okta’s resource connection.
- Enter Client ID at the upstream server, and its secret if the client is confidential.
- Save, then verify the client as yourself. Bifrost runs both exchanges with your own SSO session and discovers the server’s tools.
The example uses Linear, which needs its own setup before it accepts ID-JAGs from Okta. In Linear, configure SAML through Okta’s app catalog (OIN), turn on MCP enterprise managed authentication at the bottom of the Okta identity provider settings, and paste your Okta org’s issuer, for example
https://your-org.okta.com. See Linear’s MCP docs.Token lifetime and revocation
- Okta limits Cross App Access to 250 ID-JAGs per user, per resource app, per month. Bifrost asks Okta for a new ID-JAG only when the one it holds for that user and server has expired or was refused upstream; an expired upstream token is renewed by re-presenting the held ID-JAG. The ID-JAG’s lifetime, not the upstream token’s, sets how many Bifrost spends.
- Removing a user’s assignment in Okta stops new ID-JAGs immediately. An upstream token already issued keeps working until it expires, and a held ID-JAG until the upstream server refuses it or it expires.
- When a tool call is rejected with
401, Bifrost always drops both the cached token and the held ID-JAG and runs both exchanges again, so Okta re-evaluates its policy. Whether the failed call is retried depends on the tool: one the server marks as read-only, idempotent or non-destructive is retried once with the new token. Any other tool, including one with no annotations, gets the401back while the exchanges run in the background, and the next call uses the new token. - Verifying a client always runs both exchanges, so an admin can confirm Okta’s current policy. Verification stores the admin’s upstream token, and the Okta refresh token when there is one, so Bifrost can discover the server’s tools. When that upstream token expires, Bifrost first re-presents an ID-JAG it holds for the admin while one is still valid; verification’s own ID-JAG is not kept, so each verification asks Okta afresh. After that, it renews from the admin’s live Bifrost SSO session, falling back to the retained refresh token only when the admin has no session. A failed session lookup is retried later rather than falling back. Identity providers that rotate refresh tokens invalidate that retained copy at the admin’s next session refresh, which is why the live session comes first. If the admin has no session and verified with only an ID token, the credential cannot renew and the client asks to be verified again when it expires.
Troubleshooting
”Sign in to Bifrost with your identity provider”
The user has no SSO session Bifrost can use as the subject. Have them sign in to the Bifrost dashboard through Okta once.The identity provider declined to issue a token
Okta refused the ID-JAG. Check that the user is assigned to the service’s app, that the AI agent has a resource connection to it, that the Token Exchange grant is enabled, and thataudience matches the resource connection.
Upstream authorization server refused the ID-JAG
The error names the server’s reason. Common causes are a client ID mismatch between Okta’s resource connection andresource_client_id, or a user with no linked account at the service.
”does not support the jwt-bearer grant”
The authorization server at theaudience does not advertise urn:ietf:params:oauth:grant-type:jwt-bearer, so it cannot redeem an ID-JAG. If you know it does, set resource_token_endpoint explicitly.
”names issuer …, not the audience”
The metadata Bifrost fetched for theaudience names a different issuer, so Bifrost sends nothing there. Check that audience matches the issuer in Okta’s resource connection exactly. If the service really redeems ID-JAGs addressed to that audience at another server, set resource_token_endpoint to its token endpoint.
