Map your OpenFGA authorization model to WorkOS FGA resource types, roles, and permissions.
This guide helps you migrate from OpenFGA to WorkOS FGA. While both systems are inspired by Google’s Zanzibar paper, they take different approaches. OpenFGA uses relation-based access control (ReBAC) with explicit tuple storage, while WorkOS FGA uses hierarchical role-based access control (RBAC) with automatic permission inheritance.
| OpenFGA Concept | WorkOS FGA Equivalent |
|---|---|
| Types | Resource Types |
| Relations | Roles + Permissions |
| Tuples | Role Assignments |
| User sets | Organization Memberships |
Computed relations (from) | Native hierarchical inheritance |
| Contextual tuples | Check conditions in app code |
but not exclusions | Permission exclusions (coming soon) |
OpenFGA requires a schema DSL and explicit tuples for every relationship. WorkOS FGA simplifies this:
Unlike standalone authorization systems, WorkOS FGA integrates natively with the WorkOS identity platform (although it can be used standalone):
Extract domain objects from your OpenFGA type definitions. These become resource types in WorkOS FGA.
Create resource types for:
Exclude:
type user – Use Organization Memberships as subjects insteadtype group – User groups are coming soon; for now, assign roles directly to users
# OpenFGA type user type organization type workspace type project # WorkOS FGA Resource Types organization (built-in) └── workspace └── project
Navigate to Authorization > Resource Types in the Dashboard to create your hierarchy.
Map OpenFGA parent relations to WorkOS FGA parent-child resource type relationships.
type workspace relations define viewer: [user] type project relations define parent: [workspace] define viewer: viewer from parent
Create a project resource type with workspace as its parent. The parent relationship is defined at the resource type level.
When you register individual project resources instances via the API, they automatically inherit from their workspace. Permissions flow down this hierarchy without explicit tuples.
OpenFGA relations like viewer, editor, and admin become roles scoped to resource types.
type project relations define viewer: [user] define editor: [user] or viewer define owner: [user] or editor
Create roles on the project resource type:
| Role | Permissions |
|---|---|
| viewer | project:view |
| editor | project:view, project:edit |
| owner | project:view, project:edit, project:manage |
The or unions in OpenFGA become multiple permissions bundled into a single role.
Permission slug convention: Permission slugs are arbitrary text, but we recommend the pattern {resource-type}:{action} for clarity. Each permission must be explicitly scoped to a resource type in the Dashboard – see more about permissions. When a role includes permissions scoped to child resource types (like project:view on a workspace role), it grants that permission on all child resources of that type.
OpenFGA computed relations using the from keyword are replaced by native hierarchical inheritance.
type workspace relations define viewer: [user] type project relations define parent: [workspace] define viewer: viewer from parent
Create a workspace resource type with a role that includes child-type permissions:
| Role (on workspace) | Permissions |
|---|---|
| viewer | workspace:view, project:view |
When you assign workspace:viewer to a user, they automatically get project:view on all projects within that workspace. No explicit per-project tuples needed.
| OpenFGA Pattern | WorkOS FGA Equivalent |
|---|---|
| Direct user tuple | Role assignment on resource |
[type#relation] usersets | Role includes child-type permissions (automatic) |
or unions | Multiple permissions in a role |
and intersections | Check both conditions in app code |
but not exclusions | Permission exclusions (coming soon) |
OpenFGA contextual tuples allow passing runtime context with permission checks. With WorkOS FGA, handle these checks in your application code instead. This keeps the check interface simple and puts conditional logic next to the data it depends on.
// Check time-based access in your app const now = new Date(); const accessWindow = await getAccessWindow(resourceId); if (now < accessWindow.start || now > accessWindow.end) { return { authorized: false }; } // Then check FGA permissions const { authorized } = await workos.authorization.check({ organizationMembershipId, permissionSlug: 'project:view', resourceExternalId: resourceId, resourceTypeSlug: 'project', });
Not everything belongs in FGA. We recommend using FGA for lower-cardinality resources (organizations, workspaces, projects) and handling high-cardinality entities (files, messages, comments) in your application.
Syncing millions of entities into FGA creates reconciliation overhead, race conditions, and consistency challenges. Instead, check access at the parent container level and filter entities in your application.
For detailed guidance on this pattern, including interceptor examples for nested entities, see High-Cardinality Entities.
view, edit, manage)check API callsOpenFGA Check:
const { allowed } = await fga.check({ user: 'user:alice', relation: 'viewer', object: 'project:budget', });
WorkOS FGA Check:
const { authorized } = await workos.authorization.check({ organizationMembershipId: 'om_01HXYZ', // available in a session token or via the API permissionSlug: 'project:view', resourceTypeSlug: 'project', resourceExternalId: 'budget', });
type user type organization relations define admin: [user] define member: [user] or admin type workspace relations define parent_org: [organization] define viewer: [user] or member from parent_org define editor: [user] or viewer define admin: [user] or admin from parent_org type project relations define parent_workspace: [workspace] define viewer: [user] or viewer from parent_workspace define editor: [user] or editor from parent_workspace
Resource type hierarchy:
organization (built-in) └── workspace └── project
Roles for workspace:
| Role | Permissions |
|---|---|
| viewer | workspace:view, project:view |
| editor | workspace:view, workspace:edit, project:view, project:edit |
| admin | All workspace and project permissions |
Roles for project:
| Role | Permissions |
|---|---|
| viewer | project:view |
| editor | project:view, project:edit |
Organization members get workspace:viewer through an organization-level role. Workspace editors automatically get project:edit on all child projects through inheritance.