Cross-App Access (XAA) / ID-JAG in the AuthPlayground
Preserving delegated identity across enterprise AI.
When Sarah asks her AI assistant to prepare a meeting briefing for Apex Logistics, the assistant needs opportunity data from Salesforce and support history from ServiceNow. Behind the scenes, the AI assistant invokes Salesforce and ServiceNow Model Context Protocol (MCP) tools to reach those systems.
Each target system sits behind its own authorization domain, with its own policies, resources, and access tokens. The AI assistant needs a way to carry Sarah’s trusted identity into each authorization domain without making her original token valid everywhere.
This creates a practical challenge. How can Sarah’s trusted identity reach another authorization domain without misusing her original token? XAA (IETF draft) and the Identity JSON Assertion Grant (ID-JAG) profile solve this problem.
The ID-JAG model
ID-JAG is a short-lived identity assertion used to carry trusted user identity into another authorization domain. It presents Sarah, for example, as authenticated by a trusted identity provider, along with an assertion created specifically for the authorization server. The receiving resource authorization server validates the assertion and decides what access to grant. The flow looks like this:
-
Sarah initiates the request.
-
The IdP authorization server performs a token exchange to issue an ID-JAG.
-
The ID-JAG crosses the authorization boundary.
-
The resource authorization server applies local policy to evaluate the ID-JAG.
-
The resource authorization server issues a local access token.
-
The local access token is used to access the protected API.
The ID-JAG isn’t the final API access token. Its job is to carry Sarah’s trusted identity to the authorization server responsible for the target resource. That authorization server remains in control of the actual access decision.
Why alternatives fail
When building cross-domain integrations, teams might try cross-app access alternatives:
- Give every MCP tool a service account
-
This weakens user-level accountability. If the only identity available is a generic MCP service account, you lose the connection to Sarah and fail audit requirements.
- Forward Sarah’s original token everywhere
-
This breaks down across authorization domains. A target authorization domain, such as ServiceNow or Salesforce, shouldn’t have to accept a token because another system issued it. One token shouldn’t become a universal enterprise credential.
- Ask Sarah to authenticate for every target
-
Sending Sarah through a new authorization interaction every time the AI needs another system causes approval fatigue and defeats the value of the AI workflow.
ID-JAG provides a way to preserve a trusted user identity across authorization boundaries while still letting each target domain control access to its own resources.
The AuthPlayground implementation
Entities involved: Client application, IdP (authorization server), authorization server B, resource server
The AuthPlayground demonstrates the Cross-App Access (XAA) / ID-JAG flow in four steps:
-
Authenticate the user.
-
Exchange the ID token for an ID-JAG.
-
Present the ID-JAG.
-
Access the protected resource.
Step 1: Authenticate the user
Sarah authenticates through the IdP authorization server. The playground exchanges an authorization code using Proof Key for Code Exchange (PKCE):
POST /idp/token
Content-Type: application/x-www-form-urlencoded
{
"grant_type": "authorization_code",
"code": "<authorization_code>",
"redirect_uri": "<callback>",
"client_id": "playground-demo-client",
"code_verifier": "<pkce_verifier>"
}
The decoded ID token contains identity and authentication context for Sarah (demo-user@example.com):
{
"sub": "demo-user@example.com",
"email": "demo-user@example.com",
"auth_time": 1786743254,
"acr": "urn:mace:incommon:iap:silver",
"amr": ["pwd"],
"iss": "https://.../idp",
"aud": "playground-demo-client"
}
Sarah has authenticated and the enterprise knows who she is. However, no target authorization domain has granted access yet. Authentication in one domain doesn’t automatically equal authorization in another.
Step 2: Exchange the ID Token for an ID-JAG
The AI workflow needs Salesforce data protected by another authorization domain. Instead of forwarding Sarah’s original ID token, the client asks the IdP authorization server for an ID-JAG targeted to the resource authorization server:
POST /idp/token Content-Type: application/x-www-form-urlencoded grant_type=urn:ietf:params:oauth:grant-type:token-exchange subject_token=<id_token> subject_token_type=urn:ietf:params:oauth:token-type:id_token requested_token_type=urn:ietf:params:oauth:token-type:id-jag audience=https://.../as-b client_id=playground-demo-client
The key parameter is requested_token_type=urn:ietf:params:oauth:token-type:id-jag. The response confirms the issuance of an ID-JAG:
{
"access_token": "<id_jag>",
"issued_token_type": "urn:ietf:params:oauth:token-type:id-jag",
"token_type": "N_A",
"expires_in": 300
}
The JWT itself is explicitly typed:
{
"alg": "RS256",
"kid": "idp-signing-key-1",
"typ": "oauth-id-jag+jwt"
}
Notice the changes between the original token and the new ID-JAG:
Original ID Token ID-JAG
sub = Sarah -------> sub = Sarah
iss = IdP Token Exchange iss = IdP
aud = Playground client aud = Resource Authorization Server
typ = oauth-id-jag+jwt
Sarah’s original token hasn’t been copied. A new, short-lived assertion has been created specifically for the receiving authorization domain.
Step 3: Present the ID-JAG
The client presents the ID-JAG to the resource authorization server:
POST /as-b/token
Content-Type: application/x-www-form-urlencoded
grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer
assertion=<id_jag>
scope=openid read
The receiving authorization server can now evaluate questions such as:
-
Do I trust the issuer of this ID-JAG?
-
Was this assertion intended for me?
-
Which user does it represent?
-
How and when did the user authenticate?
-
What access should I grant under my own policy?
If local policy permits the request, the resource authorization server issues its own access token. The ID-JAG provides trusted identity context, but doesn’t force the target domain to grant access.
The resulting local token contains:
{
"sub": "demo-user@example.com",
"client_id": "playground-demo-client",
"scope": "openid read",
"iss": "https://.../as-b",
"aud": "https://.../rs"
}
The user identity crosses the authorization boundary. but the access decision is made locally under the policy protecting the target resource.
Step 4: Access the protected resource
The client now calls the protected API using the locally issued access token, completely independent of the original ID-JAG or ID token:
GET /rs/api/data
Authorization: Bearer <resource_access_token>
The playground returns:
{
"message": "Access granted to protected resource",
"user": "demo-user@example.com",
"scope": "openid read",
"token_type": "Bearer"
}
How XAA / ID-JAG relates to other concepts
How this works in an enterprise environment
In a real-world scenario where Sarah’s prompt requires data from both Salesforce and ServiceNow, this pattern happens independently for each system. Salesforce access is evaluated under the policy protecting Salesforce resources. ServiceNow access is evaluated under the policy protecting ServiceNow resources. Neither target needs to accept Sarah’s original token, and neither target inherits the other system’s authorization decision.
|
This example assumes a target authorization environment that supports the ID-JAG profile, rather than implying that Salesforce natively implements this flow. |
How XAA / ID-JAG relates to workload identity
The MCP tool is software performing work as part of the AI workflow. ID-JAG doesn’t turn the MCP tool into Sarah or establish the MCP tool’s workload identity. Those are separate concerns.
ID-JAG carries the trusted user identity into the authorization domain. A workload identity mechanism, such as SPIFFE / SVID, defines which software workload is running. In a production AI architecture, both ID-JAG and SPIFFE matter. Keeping these identities separate avoids making the MCP tool pretend to be Sarah.
Key takeaways
Traditional applications often have predictable integration paths. Enterprise AI can fan out dynamically. One prompt might use Salesforce, ServiceNow, Databricks, Jira, Microsoft 365, and internal APIs. Each target represents a different authorization context.
ID-JAG preserves Sarah’s trusted identity as requests cross authorization boundaries. The MCP tool remains software. Sarah remains the user. The authorization domain protecting the target resource remains in control of what access it grants. One user request reaches multiple enterprise systems while preserving user identity, local authorization control, and clear trust boundaries.