Skip to content

About

No description, website, or topics provided.

Resources

Code of conduct

Stars

1 star

Watchers

0 watching

Forks

Latest commit

 

History

17 Commits

Folders and files

Repository files navigation

Employee Onboarding - XAA Demo (TypeScript)

A Cross App Access (XAA / SEP-990) demo built with the MCP TypeScript SDK, registered as an MCP Requesting App on xaa.dev.

The scenario

A new hire signs in once with company SSO. Their onboarding checklist loads immediately, pulled live from the task system, with no "Connect your account" screens, no per-app OAuth consent flows. IT wired up the connection at the IdP level; Cross App Access propagates the user's identity across the trust boundary on each request.

The dashboard shows:

  • A progress ring and stats for the onboarding checklist
  • An Up next card suggesting the next step (required steps first)
  • The checklist itself, with priorities mapped to Required / Recommended / Optional
  • A Behind the scenes panel showing all four XAA steps live, with timings, decoded ID-JAG and access-token claims, and a button to re-run the flow via CrossAppAccessProvider

All checklist data is fetched at request time from xaa.dev's protected MCP server (todo0://todos) with read-only scopes (todos.read mcp.access). Nothing is seeded or mocked.

The XAA flow underneath

Step Protocol Endpoint SDK API
1. Company SSO OIDC auth code + PKCE idp.xaa.dev/authorize (plain OIDC)
2. ID token → ID-JAG RFC 8693 idp.xaa.dev/token discoverAndRequestJwtAuthGrant() / requestJwtAuthorizationGrant()
3. ID-JAG → access token RFC 7523 auth.resource.xaa.dev/token CrossAppAccessProvider token request
4. Fetch checklist MCP Streamable HTTP mcp.xaa.dev/mcp Client + StreamableHTTPClientTransport

The flow uses CrossAppAccessProvider: 401 → RFC 9728 discovery → assertion callback → RFC 7523 exchange → retry, fully SDK-orchestrated.

Prerequisites

  • Node.js 20 or later
  • A free xaa.dev registration (email only, no account or password)

Setup

1. Register a requesting app on xaa.dev

  1. Go to the requesting app registration page.
  2. Enter your email address. It filters which registered apps are visible to you.
  3. Select + Register New App and fill in the form:
    • Application Name: any label, for example Onboarding App - Local Dev
    • Redirect URIs: http://localhost:3001/callback (the match is exact, including scheme, host, port, and path)
    • Connect to Resource: select the Todo MCP server resource (todo0-mcp) and keep the todos.read and mcp.access scopes
  4. Save the credentials from the confirmation modal.

Registration creates two separate OAuth clients, and mixing them up is the most common XAA mistake:

Client Credentials Used in
Main client client_id / client_secret Step 1 (sign-in) and step 2 (token exchange) at the IdP
Resource client resource_client_id / resource_client_secret (the ID looks like client_xxx-at-todo0-mcp) Step 3 (JWT bearer grant) at the resource's authorization server

Why two? Step 3 crosses a trust boundary. The IdP and the resource's authorization server are separate trust domains, so your app holds a separate identity at each. Using the main client's credentials in step 3 fails with invalid_client.

2. Clone the repository

git clone https://github.com/oktadev/okta-xaa-typescript-mcp-sdk-example.git
cd okta-xaa-typescript-mcp-sdk-example

3. Configure the app

cp .env.example .env

Fill in .env with the credential pairs from the registration modal: app pair → XAA_*; token-exchange pair → EXCHANGE_* (only if your registration issued a separate pair; leave blank to fall back to the app pair); resource pair client_xxx-at-todo0-mcp → MCP_*.

4. Run the app

npm install
npm start        # http://localhost:3001

Note: uses port 3001. Stop any other app using that port before starting this one, since both share the registered redirect URI.

5. Explore the flow

  1. Open http://localhost:3001 and select Sign in with company SSO. xaa.dev's IdP, IdenX, accepts any email address, so no real credentials are involved.
  2. After sign-in, the flow runs automatically via CrossAppAccessProvider, which discovers the authorization server through RFC 9728 resource metadata (no hardcoded authorization server URL): the four steps light up in order in the Behind the scenes panel with real timings, and the checklist renders as soon as step 4 delivers the data.
  3. Select any step card to expand its decoded token. Check three things while you are there:
    • The ID-JAG's aud is the authorization server and its resource is the MCP server URL
    • The access token's scope claim contains todos.read mcp.access
    • The access token's aud matches the ID-JAG's resource, byte for byte
  4. Click 🔄 Re-run (SDK discovers the auth server) to replay the flow and see fresh timings and tokens.

Caveats

  • client_secret_post required for developer-registered clients (SDK defaults to Basic auth)
  • scope must be sent explicitly in the RFC 7523 request or the access token is issued scope-less
  • No trailing slashes in audience (exact string match at the IdP)
  • IdP publishes OIDC discovery only; the SDK's metadata-discovery fallback handles it

Files

src/config.ts        # env config (trailing-slash safe)
src/xaa.ts           # XAA steps built on the SDK (ID-JAG request, MCP fetch, CrossAppAccessProvider orchestration)
src/server.ts        # Express: OIDC login + SSE endpoint streaming steps 2–4 live
public/index.html    # Employee onboarding UI (ring, up-next, checklist, behind-the-scenes)

Links

This example uses the following open standards and libraries:

Help

Please post any questions as comments on the blog post, or visit our Okta Developer Forums.

License

Apache 2.0, see LICENSE.

About

No description, website, or topics provided.

Resources

Code of conduct

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages