# Configuring WorkOS

The MCP Gateway can use [WorkOS](https://workos.com/) as the identity provider
behind its downstream OAuth flow. The `mcp-workos-oauth-inbound` policy is a
WorkOS-friendly wrapper around the generic `mcp-oauth-inbound` policy: provide a
WorkOS client ID and client secret and the policy derives the OIDC issuer, JWKS
URL, and authorize and token URLs from the client ID.

This guide walks through the WorkOS dashboard setup, then wires the policy into
a gateway project. Read the [authentication overview](./overview.mdx) first for
the two-layer OAuth model.

## Set up WorkOS

The MCP Gateway acts as an OAuth 2.1 authorization server in front of WorkOS
AuthKit. WorkOS handles browser login; the gateway issues its own access tokens
that bind to MCP routes.

### Configure AuthKit

1. In the WorkOS Dashboard, switch to the environment you want the gateway to
   use, then open **Authentication → AuthKit**.
2. If AuthKit isn't enabled yet, complete the AuthKit setup flow first — enable
   email and password sign-in or any social connections your users need.

### Add the redirect URI

1. Open **Redirects** in the WorkOS Dashboard.
2. Add `https://<gateway-host>/oauth/callback` as an allowed redirect URI. Add
   `http://localhost:9000/oauth/callback` for local development with
   `zuplo dev`.
3. Save.

### Note the API credentials

Open **API Keys** in the WorkOS Dashboard. Copy the **Client ID** and the **API
Key** (= client secret) for the environment you're using.

## Wire the policy into the gateway

Add the policy to `config/policies.json`:

```json
{
  "name": "workos-managed-oauth",
  "policyType": "mcp-workos-oauth-inbound",
  "handler": {
    "module": "$import(@zuplo/runtime/mcp-gateway)",
    "export": "McpWorkosOAuthInboundPolicy",
    "options": {
      "clientId": "$env(WORKOS_CLIENT_ID)",
      "clientSecret": "$env(WORKOS_CLIENT_SECRET)"
    }
  }
}
```

Set the two environment variables in your project's environment configuration.
The secret values belong in the project secret store.

Attach the policy to each MCP route in `config/routes.oas.json` and register the
gateway plugin in `modules/zuplo.runtime.ts` (see
[Configuring Auth0](./configuring-auth0.mdx#wire-the-policy-into-the-gateway)
for the route and plugin patterns — they're identical across all wrappers).

## What the wrapper derives

WorkOS keys its OIDC issuer by the client ID. Given a `clientId` like
`client_01KC6057N3C66XJAXZ65YHAC72`, the wrapper derives:

| Generic field           | Derived value                                         |
| ----------------------- | ----------------------------------------------------- |
| `oidc.issuer`           | `https://api.workos.com/user_management/{clientId}`   |
| `oidc.jwksUrl`          | `https://api.workos.com/sso/jwks/{clientId}`          |
| `browserLogin.url`      | `https://api.workos.com/user_management/authorize`    |
| `browserLogin.tokenUrl` | `https://api.workos.com/user_management/authenticate` |

These endpoint shapes come from WorkOS OIDC discovery at
`https://api.workos.com/user_management/{clientId}/.well-known/openid-configuration`.

## Test the configuration

The fastest sanity check is to connect an MCP client:

1. Open Claude Desktop, Cursor, Claude Code, or another OAuth-aware MCP client.
2. Add a remote MCP server pointing at one of your `/mcp/{slug}` routes.
3. The client should redirect you to the WorkOS AuthKit sign-in page. After
   login, the gateway's consent screen renders. Approve it.
4. The client receives an access token and can call `tools/list`.

If something fails partway through, walk the flow manually using the
[manual OAuth testing guide](./manual-oauth-testing.mdx) — it exercises every
endpoint with `curl` so you can see the raw responses.

## Common issues

- **`Invalid redirect URI`.** The redirect URI on **Redirects** in the WorkOS
  Dashboard doesn't match `https://<gateway-host>/oauth/callback` exactly.
- **`invalid_client`.** The client secret value doesn't match. WorkOS shows the
  secret only once after creation — regenerate it from **API Keys** if you've
  lost it.
- **JWKS fetch fails.** The client ID doesn't match the API key's environment.
  Make sure both `clientId` and `clientSecret` come from the same WorkOS
  environment.

## Related

- [Authentication overview](./overview.mdx)
- [Configuring a generic OIDC provider](./configuring-generic-oidc.mdx)
- [Per-user OAuth to upstream MCP servers](./upstream-oauth.mdx)
