Skip to main content
The Cross App Access Flow is only applicable for multi-tenant auth uses of Connected Apps.
The Cross App Access (XAA) Flow allows a Connected App to exchange a JWT issued by an external workforce identity provider (IdP) for a Stytch access token. This enables service-to-service and agent-to-service flows where the user has already authenticated with their organization’s IdP (e.g. Okta, Microsoft Entra, Google Workspace) and needs to access resources protected by Stytch Connected Apps — without a browser-based redirect. This flow uses the urn:ietf:params:oauth:grant-type:jwt-bearer grant type and implements the Identity Assertion Authorization Grant (ID-JAG) draft specification.

When to use the XAA Flow

Use the Cross App Access Flow when:
  • Your application needs to act on behalf of users who have already authenticated with a workforce IdP
  • You want to avoid browser redirects (e.g. in CLI tools, background services, or AI agents)
  • Your users sign in via OIDC SSO and you want Connected Apps to recognize them without a separate authorization code flow

Prerequisites

  • A B2B Stytch project with Connected Apps enabled
  • An OIDC SSO connection configured for the organization, pointing to the external IdP
  • A Confidential Connected App client (first-party or third-party)

How it works

1

User authenticates with the workforce IdP

The user authenticates with their organization’s workforce identity provider (e.g. Okta, Microsoft Entra) via OpenID Connect. After authentication, the Connected App receives an ID token representing the user’s session with the IdP.
2

Connected App requests an ID-JAG from the IdP

The Connected App makes a Token Exchange request to the workforce IdP’s token endpoint to obtain an ID-JAG token:
curl -X POST https://your-idp.example.com/oauth2/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=urn:ietf:params:oauth:grant-type:token-exchange" \
  -d "requested_token_type=urn:ietf:params:oauth:token-type:id-jag" \
  -d "subject_token=<ID_TOKEN>" \
  -d "subject_token_type=urn:ietf:params:oauth:token-type:id_token" \
  -d "audience=<STYTCH_PROJECT_ISSUER_URL>" \
  -d "scope=openid email profile"
The IdP validates the subject token, evaluates any administrator-defined policies, and issues a signed ID-JAG token with typ: "oauth-id-jag+jwt" in the header.
3

Connected App exchanges the ID-JAG for a Stytch access token

The Connected App sends the ID-JAG token to Stytch’s token endpoint using the XAA Flow’s grant type:
curl -X POST https://<tenant>.customers.stytch.dev/v1/oauth2/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer" \
  -d "assertion=<ID_JAG_TOKEN>" \
  -d "client_id=<CONNECTED_APP_CLIENT_ID>" \
  -d "client_secret=<CONNECTED_APP_CLIENT_SECRET>" \
  -d "scope=openid email profile"
4

Stytch validates and issues access token

Stytch validates the ID-JAG token by:
  1. Matching the token’s iss claim to a configured OIDC connection
  2. Verifying the token signature against the OIDC connection’s JWKS endpoint
  3. Validating that aud contains the Stytch project’s issuer URL
  4. Confirming the client_id claim matches the requesting Connected App
  5. Resolving the token’s sub claim to a member (see below)
If all checks pass, Stytch issues a Connected Apps access token scoped to the resolved member.

Member resolution

Stytch resolves the ID-JAG token’s sub claim to a member using two mechanisms, checked in this order:
  1. OIDC registration match: Stytch looks for an active OIDC member registration on the same connection where the provider_subject matches the token’s sub. This is automatically created when a member signs in via OIDC SSO — no additional configuration is needed.
  2. External ID match: If no OIDC registration is found, Stytch checks for a member in the organization whose external_id matches the token’s sub.
If your members sign in via OIDC SSO before using the XAA Flow, they will be resolved automatically through their OIDC registration. No manual external_id mapping is required.

ID-JAG token format

The ID-JAG token must be a signed JWT with the following structure: Header:
{
  "alg": "RS256",
  "typ": "oauth-id-jag+jwt",
  "kid": "<KEY_ID>"
}
Claims:
ClaimDescription
issThe IdP’s issuer URL (must match the OIDC connection’s issuer)
subThe user’s identifier at the IdP
audMust include your Stytch project’s issuer URL
client_idThe Connected App’s client ID
scopeRequested scopes (space-delimited)
expExpiration time
iatIssued-at time
jtiUnique token identifier

Token exchange response

A successful exchange returns a standard OAuth 2.0 token response:
{
  "access_token": "eyJ...",
  "token_type": "bearer",
  "expires_in": 3600,
  "scope": "openid email profile"
}