Okta SAML and Keycloak for ID-JAG Cross App Access Okta SAML and Keycloak can interoperate via the Identity Assertion JWT Authorization Grant (ID-JAG) draft to enable cross-app access, according to a demo by Christian Posta. The flow converts enterprise SAML assertions into scoped OAuth tokens for external APIs without user-facing consent screens. The demo uses Okta as the enterprise IdP and Keycloak as the resource authorization server, following the ID-JAG SAML profile. Okta SAML and Keycloak for ID-JAG Cross App Access A working Okta SAML/SSO → ID-JAG → Keycloak access-token path for Cross-App Access The Identity Assertion JWT Authorization Grant ID-JAG https://datatracker.ietf.org/doc/draft-ietf-oauth-identity-assertion-authz-grant/ draft, which is the core of Cross-App Access XAA https://xaa.dev , standardizes how enterprises broker access to APIs and MCP resources across identity boundaries think “calling from enterprise apps to SaaS APIs and MCP servers” . Enterprises already use their Enterprise IdP for SSO, but SSO only identifies the user. It says nothing about that user’s service-specific permissions on GitHub, Asana, Figma, and so on. You might say “we already use SSO on those apps, why is that not enough” … and you do, for login usually to some UI . Those apps still need scoped OAuth access tokens for API access, not just an identity assertion. ID-JAG is the bridge: convert enterprise user identity into those scoped tokens, under IdP policy. The flow looks like this: - The user signs into enterprise app/agent via SSO OIDC ID Token or SAML assertion , so the app knows who they are. - When the app needs to call an external resource SaaS API, MCP server, etc. , it exchanges that identity assertion at the enterprise IdP for an intermediate ID-JAG via OAuth 2.0 Token Exchange https://datatracker.ietf.org/doc/html/rfc8693 . This is where the enterprise decides whether to allow the access, what scopes to grant, and how to manage the authorization lifecycle. - The app presents the ID-JAG to the resource authorization server as a JWT authorization grant https://datatracker.ietf.org/doc/html/rfc7523 and receives a provider-scoped access token. - The app uses that access token to call the resource MCP tools, APIs, etc. . The user never sees an OAuth consent screen at the SaaS app. Access is pre-decided by the enterprise IdP and controlled by admin policy. I recently dug into a working example with Okta as the enterprise IdP and Keycloak as the resource/service IdP. Since ID-JAG is an open OAuth draft / spec, the most important part of the story is interoperability. And to take it one step further: although the happy path in the draft primarily uses OIDC for user identity assertions, enterprises still standardize around SAML. So let’s build an Okta SAML-based requesting app, follow the ID-JAG SAML profile in the draft, and redeem that grant at a different identity domain: Keycloak standing in for the resource authorization server. Everything here is reproducible from christian-posta/okta-saml-idjag https://github.com/christian-posta/okta-saml-idjag . TL;DR here is a demo video of this blog: Actors in this demo Worth naming explicitly up front, because “app” and “agent” blur quickly: | Role | In this demo | What it does | |---|---|---| User | Okta test user mcpuser@… | Authenticates via SAML SSO | Requesting app | Local demo app localhost:4141 | Holds the SAML session, drives both token exchanges, calls the resource | Enterprise IdP | Okta Integrator / XAA early access | Issues SAML assertions and ID-JAGs under admin policy | AI Agent OAuth client | Okta “AI Agent” registration | The private key jwt client that actually calls Okta’s token endpoint | Resource AS | Keycloak realm idjag-resource | Trusts Okta’s ID-JAG, mints its own access token | Resource client | idjag-demo-client | Client that presents the ID-JAG at Keycloak client id claim must match | The requesting app and the Okta AI Agent are related but not the same object. The user signs into the SAML app; the AI Agent is the OAuth client allowed to exchange that identity for an ID-JAG. The end-to-end path including the SAML hop OIDC-shaped XAA is often drawn as one token exchange at the IdP, then a JWT bearer grant at the resource. With SAML, the draft adds a pre-step: turn the SAML assertion into a refresh token first, then exchange that refresh token for the ID-JAG. sequenceDiagram actor User participant App as Requesting app participant Okta as Okta enterprise IdP participant KC as Keycloak resource AS participant API as Resource API User- App: Login App- Okta: SAML AuthnRequest Okta-- App: SAML Assertion Note over App,Okta: Leg 1 — SAML → refresh token App- Okta: Token Exchange