Use FGA with AuthKit for role-aware sessions and seamless authorization.
FGA integrates with AuthKit to provide two layers of authorization. Organization-scoped roles and permissions are embedded directly in the session token for instant checks. Resource-scoped permissions are checked through the Authorization API.
When a user authenticates, their session token includes organization-scoped role information:
{ sub: "user_01HXYZ..." org_id: "org_01HXYZ..." role: "org_member" permissions: ["org:view", "workspace:view", "project:view"] }
Your application can check these permissions directly from the token without making API calls.
Resource-scoped roles are intentionally excluded from the JWT:
For resource-level permissions, use the Authorization API – it’s fast and always current.
FGA role assignments are scoped to organization memberships, not users directly. When you assign a role on a resource, the subject is the organization membership ID (om_...), which represents a user’s relationship to a specific organization.
This means FGA access checks require the organization membership ID rather than the user ID. You can include this ID in your JWT using JWT Templates:
{ "organization_membership_id": "{{ organization_membership.id }}" }
This produces a token with the membership ID alongside the default claims:
{ "sub": "user_01HXYZ...", "org_id": "org_01HXYZ...", "role": "org_member", "permissions": ["org:view", "workspace:view"], ... "organization_membership_id": "om_01HXYZ..." }
Your application can then extract the membership ID from the JWT and pass it to FGA access checks without needing an additional API call.
Organization layer (JWT) – Check the token directly for org-wide features like navigation and settings. Instant, no API call needed.
Resource layer (API) – Call the Authorization API for specific resource access. Checks the full permission hierarchy including inheritance.
Can this user edit this project? 1. Quick check: Does the JWT include project:edit for all projects? → If yes, authorized (no API call) 2. Otherwise: Call the API for this specific project → Checks direct assignments and inheritance
Groups let you assign resource-scoped roles to a set of users at once. Instead of creating individual role assignments for each organization membership on a resource, assign the role to a group and let WorkOS propagate it to all members.
curl https://api.workos.com/authorization/groups/group_01HXYZ/role_assignments \ -X POST \ -H "Authorization: Bearer sk_example_123456789" \ -H "Content-Type: application/json" \ -d '{ "role_slug": "workspace-admin", "resource_type_slug": "workspace", "resource_external_id": "ws-engineering" }'
Every member of the group gains the workspace-admin role on the Engineering workspace. Permissions propagate through the resource hierarchy just like direct assignments, so members also get inherited access to child resources (projects, apps, etc.).
Resource-scoped roles from group role assignments are checked through the Authorization API the same way as direct assignments. The access check evaluates all role sources for the organization membership, including group-sourced roles:
Alice's effective roles: ├─ org-member (direct assignment) ├─ workspace-admin on Workspace: Engineering (from "Engineering" group) └─ project-viewer on Project: Sensitive (direct assignment)
A single check call returns the combined result across all sources. Your application does not need to distinguish between direct and group-sourced roles.
Roles update when group membership or group role assignments change:
This makes groups ideal for managing team-level access. When an engineer joins the platform team, adding them to the “Platform” group grants access to all the team’s workspaces and projects without individual assignments.
Consider a project management application where you want all engineers to have the editor role on a shared workspace:
editor on the workspace resource to the group
# 1. Create the group curl https://api.workos.com/organizations/org_01HXYZ/groups \ -X POST \ -H "Authorization: Bearer sk_example_123456789" \ -H "Content-Type: application/json" \ -d '{"name": "Engineering"}' # 2. Add a member curl https://api.workos.com/organizations/org_01HXYZ/groups/group_01HXYZ/organization-memberships \ -X POST \ -H "Authorization: Bearer sk_example_123456789" \ -H "Content-Type: application/json" \ -d '{"organization_membership_id": "om_01HXYZ"}' # 3. Assign the role to the group curl https://api.workos.com/authorization/groups/group_01HXYZ/role_assignments \ -X POST \ -H "Authorization: Bearer sk_example_123456789" \ -H "Content-Type: application/json" \ -d '{ "role_slug": "editor", "resource_type_slug": "workspace", "resource_external_id": "ws-engineering" }'
Every member of the “Engineering” group now has the editor role on the Engineering workspace. When a new engineer joins, adding them to the group grants the role automatically.
For full details, see Group role assignments.