<!-- llms.txt: https://workos.com/llms.txt -->

# API Gateway

## Introduction

The API Gateway is a managed reverse proxy that sits in front of your API. Requests arrive on a WorkOS-managed gateway domain, or on your own custom domain, where the gateway authenticates the caller — using an [API key](https://workos.com/docs/authkit/api-keys) or an AuthKit session — mints a short-lived, signed identity assertion, and forwards the request to your origin.

Your origin then trusts a single WorkOS-signed assertion instead of re-implementing authentication on every request.

> **Note:** The API Gateway is available in beta. Contact WorkOS to enable it for your
> environment.

## How it works

1. A client sends a request to your gateway domain, including its credentials — an API key or an AuthKit session.
2. The gateway authenticates the caller at the edge.
3. The gateway signs a short-lived assertion describing the caller and attaches it as the `X-WorkOS-Gateway-Assertion` header.
4. The gateway proxies the request to your origin over HTTPS.

The assertion is your origin's perimeter: it proves the request passed through the gateway and carries the caller's verified identity. The gateway strips any client-supplied `X-WorkOS-Gateway-Assertion` header before signing its own, so a caller can't forge one.

## The gateway assertion

The `X-WorkOS-Gateway-Assertion` header contains a JSON Web Token (JWT) signed with RS256 using a per-environment key. Each assertion is valid for approximately 60 seconds.

The token includes the standard registered claims:

| Claim  | Description                                                              |
| ------ | ------------------------------------------------------------------------ |
| `iss`  | The issuer — your gateway's URL.                                         |
| `aud`  | The audience configured for your gateway, usually the same as your gateway URL. |
| `sub`  | The authenticated principal. Absent for anonymous requests.              |
| `iat`  | The time the assertion was issued.                                       |
| `exp`  | The expiration time, approximately 60 seconds after `iat`.               |
| `jti`  | A unique identifier for the assertion.                                   |

It also includes WorkOS-specific claims that describe the caller:

| Claim          | Description                                                            |
| -------------- | ---------------------------------------------------------------------- |
| `subject_type` | `api_key`, `authkit_session`, or `anonymous`.                          |
| `org_id`       | The organization the caller belongs to, when applicable.              |
| `user_id`      | The user id, for an API key owned by a user.                          |
| `permissions`  | The permissions granted to the caller, when applicable.               |
| `roles`        | The roles granted to the caller, for AuthKit sessions.                |

The claims present depend on `subject_type`:

- **`api_key`** — `sub` is the API key id, with `org_id`, plus `user_id` and `permissions` when the key has them.
- **`authkit_session`** — `sub` is the user id, with `org_id`, `roles`, and `permissions` when present.
- **`anonymous`** — only `subject_type` is set. The request reached the gateway without credentials, and your origin decides whether to allow it.

## Verifying the assertion at your origin

Verify the assertion on every request, and reject any request that doesn't carry a valid one.

1. Read the `X-WorkOS-Gateway-Assertion` header.
2. Fetch the per-environment JSON Web Key Set (JWKS) from `https://<your-gateway-domain>/.well-known/jwks.json`.
3. Verify the token's signature, along with its `iss`, `aud`, and `exp`.
4. Authorize the request using the verified claims.

> **Note:** Always verify both `iss` and `aud`. This confirms the assertion was issued for
> your environment and intended for your origin, which prevents an assertion
> issued for one environment from being replayed against another.

```ts title="Verify a gateway assertion (Node.js)"
import { createRemoteJWKSet, jwtVerify } from 'jose';

// Your gateway domain issues the assertion and serves the per-environment JWKS.
const GATEWAY_URL = 'https://api.example.com';
// The audience configured for your gateway — usually the same as your gateway URL.
const AUDIENCE = GATEWAY_URL;

const jwks = createRemoteJWKSet(
  new URL(`${GATEWAY_URL}/.well-known/jwks.json`),
);

async function verifyGatewayAssertion(request) {
  const token = request.headers['x-workos-gateway-assertion'];
  if (!token) {
    throw new Error('Missing gateway assertion');
  }

  const { payload } = await jwtVerify(token, jwks, {
    issuer: GATEWAY_URL,
    audience: AUDIENCE,
  });

  // payload.subject_type, payload.sub, payload.org_id, payload.permissions, ...
  return payload;
}
```

## Using the identity

Authorize each request from the verified claims. Switch on `subject_type` to handle API keys, AuthKit sessions, and anonymous traffic, then use `sub`, `org_id`, and `permissions` or `roles` to make an authorization decision. Because the gateway has already authenticated the caller, your origin only needs to authorize the identity in the assertion.
