Repository navigation
feat: support provider-managed Cross-App Access (XAA) token flows #2637
Description
Problem Statement
OpenShell can safely custody provider credentials and inject a final bearer token only into approved sandbox egress requests, but it has no supported way to perform a Cross-App Access (XAA) flow end to end.
XAA lets a requesting application act on a user's behalf against a separately administered resource application without passing a long-lived credential or final API token through the sandbox. The closed experimental proof of concept in #1682 demonstrated this with Okta and xaa.dev, but there is no replacement or tracking issue.
The currently merged NemoClaw reference, NVIDIA/NemoClaw#7265, covers the narrower protected-API case: provider-managed refresh plus endpoint-scoped bearer injection. It does not perform OBO or XAA.
Proposed Design
Provide a provider-managed XAA grant chain that keeps all subject material, client authentication material, intermediate assertions, and final API tokens outside the sandbox.
- Obtain a fresh user subject token through a generic dynamic identity source.
- At the identity provider token endpoint, exchange that subject token using RFC 8693 for an Identity Assertion JWT Authorization Grant (ID-JAG). The requesting application authenticates with its own registered client relationship.
- At the resource authorization server token endpoint, redeem the ID-JAG through the RFC 7523 JWT bearer grant. The resource application authenticates with its independent client relationship.
- Store/cache the resulting resource access token under provider custody and inject it only for L7-approved destinations and processes.
The provider contract should model the two stages explicitly: distinct token endpoints, grant types, audiences/resources/scopes, and client authentication material or platform identity. It should also define rotation, caching, expiry, revocation/cleanup, and fail-closed behavior. A blueprint or sandbox may name a provider and credential placeholder, but must not contain OAuth client secrets, subject tokens, ID-JAGs, or final bearer tokens.
Alternatives Considered
- Reuse feat(provider): support for SPIFFE backed token exchange #1970 unchanged. feat(provider): support for SPIFFE backed token exchange #1970 is useful related work, but its current two-stage flow uses RFC 8693 at one token endpoint with SPIFFE JWT-SVID client assertions. XAA requires an RFC 8693 ID-JAG issuance step followed by an RFC 7523 JWT-bearer step, usually at a separate resource authorization server with separate client registration.
- Handle XAA in a middleware. Egress middleware intentionally cannot read or replace supervisor-owned Authorization headers, so it is not a credential custody or exchange layer.
- Give the sandbox the subject token or resource token. This bypasses OpenShell's provider boundary and makes refresh, revocation, and destination restriction harder to enforce.
- Reopen feat(xaa): add sample xaa provider flow #1682 as-is. That branch was an Okta/xaa.dev-specific proof of concept; the supported surface should be a generic provider capability.
Agent Investigation
- feat(xaa): add sample xaa provider flow #1682 is the only OpenShell XAA-specific implementation found. It is a closed draft and implemented the expected four-step flow: user authentication, RFC 8693 exchange for an ID-JAG, RFC 7523 redemption at the resource authorization server, and scoped injection of the resulting API token.
- feat(providers): add dynamic identity sources to Oauth2TokenExchange strategy #1736 tracks generic dynamic identity sources required to obtain a user subject token safely.
- Tracking: generalize brokered credential delivery beyond inference.local and Microsoft S2S #1755 tracks the broader credential-broker direction.
- feat(providers): support SPIFFE-backed token grants #1784 merged SPIFFE-backed dynamic grants and endpoint-bound injection. It is a useful workload-identity and delivery primitive, but does not represent the user or produce an ID-JAG.
- feat: support user-subject dynamic token grants for sandbox agents #1987 and the open feat(provider): support for SPIFFE backed token exchange #1970 track a related user-plus-sandbox delegated identity path. They do not currently model XAA's two authorization servers or its RFC 8693 then RFC 7523 grant chain.
- The relevant external protocol is the IETF Identity Assertion JWT Authorization Grant draft: https://datatracker.ietf.org/doc/html/draft-ietf-oauth-identity-assertion-authz-grant
Glossary
- XAA (Cross-App Access) — A delegated OAuth flow in which one application acts for a user against a separately administered application.
- ID-JAG — Identity Assertion JWT Authorization Grant. A signed intermediate JWT derived from the user's identity and redeemed at the resource application's authorization server. It is not the final API access token, but it remains sensitive authorization material.
- OBO (on behalf of) — The general pattern of one party acting with delegated authority from another. OBO is a use case, not one specific protocol.
- RFC 8693 — OAuth Token Exchange. In this flow it exchanges the user's subject token for an ID-JAG.
- RFC 7523 — The OAuth JWT profile. In this flow it is used to redeem the ID-JAG and may also be used for client authentication.
- Subject token / actor token — RFC 8693 inputs representing the original subject, normally the user, and the acting party, such as an agent or workload.
sub/act.sub— Token claims identifying the subject and the actor acting for that subject.- SPIFFE / SVID — SPIFFE is a workload-identity standard; an SVID is its short-lived identity document. A JWT-SVID can authenticate the sandbox to a broker without placing a client secret in the sandbox.
- Workload identity vs. agent identity — A workload identity identifies the running sandbox or service. Agent identity additionally requires a binding to the logical agent, its owner, and its delegated permissions.
- Dynamic identity source — The proposed OpenShell mechanism for obtaining fresh user or workload identity material at exchange time instead of storing an expiring token in provider configuration.
- Credential custody — Keeping subject tokens, client authentication material, ID-JAGs, and final access tokens outside the agent, with OpenShell injecting only the final credential into an approved request.
- Provider-managed flow — The OpenShell provider or broker performs the exchanges, caching, rotation, and injection; the sandbox names the provider but never receives the underlying token material.
Related Work
- feat(xaa): add sample xaa provider flow #1682 — closed XAA proof of concept
- feat(providers): add dynamic identity sources to Oauth2TokenExchange strategy #1736 — dynamic identity sources for token exchange
- Tracking: generalize brokered credential delivery beyond inference.local and Microsoft S2S #1755 — credential broker
- feat(providers): support SPIFFE-backed token grants #1784 — SPIFFE-backed dynamic token grants
- feat(provider): support for SPIFFE backed token exchange #1970 — SPIFFE-backed token exchange
- feat: support user-subject dynamic token grants for sandbox agents #1987 — user subject plus sandbox-agent identity
- feat(blueprint): add Okta runtime identity reference NemoClaw#7265 — protected API runtime-identity reference
Activity
afourniernv commentedon Aug 19, 2026
This came up again in a meeting with Astra about XAA and OpenShell. The discussion reinforced that OpenShell has useful enforcement and credential-delivery primitives, but still lacks a supported end-to-end provider-managed XAA flow for user-to-agent delegation. Adding this as another data point that the gap remains relevant.
afourniernv commentedon Aug 19, 2026
To clarify the relationship with SPIFFE: SPIFFE and XAA solve different parts of this flow. A SPIFFE JWT-SVID can authenticate the sandbox or workload to the identity broker without placing a client secret in the sandbox. XAA then handles user delegation, producing a scoped downstream token representing both the user and the agent.
The merged SPIFFE token-grant work in #1784 covers workload authentication only; it does not carry a user subject or perform XAA. A complete flow still needs to bind the SPIFFE workload identity to a registered agent, combine it with a fresh user identity, and perform the RFC 8693/ID-JAG and RFC 7523 exchanges described here.
github-actions commentedon Sep 3, 2026
This issue has had no activity for 14 days and is now marked stale. It may be closed in 7 days if there is no further activity. Comment or remove the state:stale label to keep it open.