> ## Documentation Index
> Fetch the complete documentation index at: https://engineering.unkey.com/llms.txt
> Use this file to discover all available pages before exploring further.

# WorkOS permissions

> How WorkOS permission strings map to Unkey resource permissions

WorkOS is authoritative for user roles and permission strings. Unkey uses WorkOS
for authentication and role assignment, then translates the WorkOS permission
strings from the access token into canonical Unkey resource permissions before
the API constructs the principal.

This translation exists because WorkOS permission strings cannot represent
Unkey resource permissions directly. WorkOS permission slugs are capped at 48
characters and cannot contain `/`, so they cannot encode canonical Unkey
resource paths. The WorkOS permission model is intentionally much smaller than
Unkey's resource permission model.

The exact list of supported WorkOS permissions and their Unkey translations is
defined in
[`pkg/auth/workos/permissions.go`](https://github.com/unkeyed/unkey/blob/main/pkg/auth/workos/permissions.go).
That Go file is the source of truth.

## Permission shape

WorkOS recommends clear, concise permission slugs with a resource and action
delimiter. Unkey follows that shape and keeps WorkOS permissions broad:

```plaintext theme={"theme":"kanagawa-wave"}
{area}:{action}
```

For example:

```plaintext theme={"theme":"kanagawa-wave"}
keys:create
```

The WorkOS slug is intentionally not a Unkey resource permission. It is stable
input from the identity provider. The API translates it after token verification
because only Unkey owns the resource-name contract.

## Translation

The WorkOS resolver wraps the generic JWT JWKS resolver. The generic resolver
verifies issuer, audience, signature, and time claims, and builds the initial
JWT principal. The WorkOS wrapper then replaces the raw WorkOS permission
strings with canonical permissions in this format:

```plaintext theme={"theme":"kanagawa-wave"}
unkey:v1:{workspace_id}:{resource_path}#{action}
```

A WorkOS permission string such as:

```plaintext theme={"theme":"kanagawa-wave"}
deployments:create
```

becomes a resource permission for the principal's workspace:

```plaintext theme={"theme":"kanagawa-wave"}
unkey:v1:{workspace_id}:projects/**#create_deployment
```

The translated permission is the only value handlers see. Handler authorization
does not depend on WorkOS permission names.

## Unknown permissions

Unknown WorkOS permission strings are ignored during translation. They don't
grant access, and they don't fall back to legacy wildcard permissions.

This makes WorkOS safe to contain permissions that are not yet understood by
the API. Adding a new permission requires updating the mapping table in
`pkg/auth/workos/permissions.go` and adding tests for the resulting Unkey
resource permission.

## Ownership boundaries

The generic JWT package must stay provider-neutral. It verifies JWTs and
returns claims as-is.

WorkOS-specific behavior belongs in `pkg/auth/workos`:

* the WorkOS issuer constant,
* the WorkOS JWKS resolver wrapper,
* the WorkOS permission mapping table, and
* tests for WorkOS permission translation.

API configuration uses the WorkOS wrapper only for JWKS-backed WorkOS access
tokens. HMAC JWT auth continues to use the generic JWT resolver directly.
