Access token claims
Understand Connect access token claims and authorize requests to your API or MCP server.
A Connect access token is a JWT that authorizes an application to call your API or MCP server. Use its claims to identify the caller, check the client’s scopes, and resolve user permissions when access depends on the user’s role.
For tokens that authenticate users in your own application, see Session tokens.
Connect issues user tokens to OAuth applications and MCP clients, including those registered through Dynamic Client Registration (DCR) or Client ID Metadata Document (CIMD). User tokens are issued through the authorization code, refresh token, and device code grants. M2M applications use the client credentials grant to obtain tokens without a user.
| Claim | User token | M2M token |
|---|---|---|
iss |
AuthKit domain | AuthKit domain |
aud |
Resource or environment client ID | Environment client ID |
sub |
User ID | Application client ID |
client_id |
Requesting application client ID | Application client ID |
org_id |
Organization selected at authorization | Application’s organization |
sid |
Consent ID | Not included |
scope |
Granted scopes | Granted scopes |
jti |
Token ID | Token ID |
exp |
Expiration time | Expiration time |
iat |
Issue time | Issue time |
The issuer is the HTTPS URL of your AuthKit domain. For user tokens, the audience is the requested resource indicator, or the environment client ID when no resource is configured. The M2M audience is not configurable per application. Granted scopes are a space-delimited string.
User tokens can include additional claims from your environment’s JWT template. M2M tokens do not support JWT templates or custom claims.
Check the client’s scopes to determine what the calling application is allowed to do. Scopes do not enforce the user’s role-based permissions, so a scoped request also needs a user-permission check when access varies by user.
Connect tokens do not include a permissions claim. For user-specific authorization, choose one of these approaches:
- Add roles with a JWT template if your server maintains the role-to-permission mapping.
- Check permissions server-side if WorkOS maintains the mapping. Use this approach if you are unsure which to choose.
For an OAuth application created in the WorkOS dashboard, assign permissions from your environment to the application as scopes. Requesting an unassigned scope fails at the authorization endpoint with invalid_scope; assign that scope or remove it from the request.
Clients registered through Dynamic Client Registration or Client ID Metadata Document cannot have scopes assigned per client. Updating their scopes returns HTTP 422 with Cannot update scopes for dynamically registered applications. These clients receive the standard OpenID Connect scopes: openid, profile, email, and offline_access.
For additional permission scopes on dynamically registered clients, contact support about an environment-wide default set. These scopes still do not restrict access based on the user’s role.
Add organization_membership.roles to your environment’s JWT template to put the user’s role slugs on every Connect user token:
Permissions are not in the template context. A template that references organization_membership.permissions fails validation with Invalid path. One template applies to the whole environment, so these claims also land on your AuthKit session tokens, which are limited to 3072 bytes of rendered template output.
After verifying the token, use sub and org_id to resolve the user’s effective permissions from the WorkOS API, then gate the request on the result. Cache the result per user and organization so each request doesn’t call the API; a role change is reflected when the cache entry expires.
- Verify the JWT against
https://authkit_domain/oauth2/jwksand checkissandaud. - Call List organization memberships with
user_idset tosubandorganization_idset toorg_id. The membership carriesrole.slug, androles[]when multiple roles are enabled. - For each role slug, call Get a custom role with
org_idand the slug. It returns the role’spermissionsfor both environment roles and organization custom roles. - Take the union of the permission slugs and reject the request with
403if the required permission is missing.
A token without org_id belongs to a user who has no organization membership, so there are no roles to resolve. Reject it, or treat it as having no permissions.
To map the calling organization to an identifier in your system, read org_id from the token and call Get an organization once to read external_id or metadata, then persist the mapping. Refresh the cached mapping when the organization’s identifiers change.