workos-fga
npx skills add https://github.com/workos/skills --skill workos-fga
Agent 安装分布
Skill 文档
WorkOS Fine-Grained Authorization
Step 1: Fetch Documentation (BLOCKING)
STOP. Do not proceed until complete.
WebFetch: https://workos.com/docs/fga/index
The documentation is the source of truth. If this skill conflicts with docs, follow docs.
CRITICAL PRE-RELEASE NOTE: FGA is scheduled for Q1 2026 release. Check docs for current availability status. If endpoints return 404 or “not available”, this feature is not yet released.
Step 2: Pre-Flight Validation
Check FGA Availability
# Test if FGA endpoints are live
curl -f -H "Authorization: Bearer ${WORKOS_API_KEY}" \
https://api.workos.com/authorization/resources 2>/dev/null && echo "FGA Available" || echo "FGA Not Yet Released"
If command fails: FGA is not available yet. Stop here.
Environment Variables
Check environment for:
WORKOS_API_KEY– starts withsk_WORKOS_CLIENT_ID– starts withclient_
# Verify keys exist and have correct prefixes
[[ $WORKOS_API_KEY == sk_* ]] && echo "API key valid" || echo "FAIL: Invalid API key"
[[ $WORKOS_CLIENT_ID == client_* ]] && echo "Client ID valid" || echo "FAIL: Invalid client ID"
SDK Verification
# Confirm WorkOS SDK is installed
npm list @workos-inc/node 2>/dev/null || echo "FAIL: SDK not installed"
If SDK missing: Install with npm install @workos-inc/node before continuing.
Step 3: Resource Type Design (CRITICAL PLANNING PHASE)
STOP. Design your resource hierarchy before writing code.
FGA extends RBAC with resource instances. You must define:
- Resource types – categories of objects (workspace, project, app)
- Hierarchy – parent-child relationships
- Permissions – which roles can do what on each type
Example Hierarchy
Organization (always root)
|
+-- Workspace
|
+-- Project
|
+-- App
Configure in Dashboard
- Navigate to WorkOS Dashboard â Authorization â Resource Types
- Create each resource type with:
slug– machine name (workspace, project, app)name– human-readable labelparent_type– optional, for inheritance
- Add permissions for each type
Verify dashboard config complete before Step 4.
Step 4: Resource Instance Registration
When users create resources in your app, register them with FGA.
Decision Tree: When to Register
User creates object in your app?
|
+-- Is it authorization-relevant? (workspace, project, NOT log entry)
| |
| +-- YES --> Register resource instance
| |
| +-- NO --> Do not register
|
+-- Is parent resource known?
|
+-- YES --> Include parent_resource_id in registration
|
+-- NO --> Register at org level (parent_resource_id = org_id)
Registration Pattern
// After creating resource in your database
const resource = await workos.fga.resources.create({
resource_type: 'workspace', // matches dashboard slug
external_id: dbWorkspace.id, // YOUR database ID
name: dbWorkspace.name,
parent_resource_id: orgId, // or parent workspace ID
});
// Store resource.id in your database alongside dbWorkspace.id
// You'll need both IDs for future operations
CRITICAL: Store both external_id (your ID) and resource.id (WorkOS ID) in your database. You need WorkOS ID for assignments.
Bulk Registration Pattern
If migrating existing resources:
// Fetch existing resources from your DB
const workspaces = await db.workspace.findMany();
// Register each with FGA
for (const ws of workspaces) {
const resource = await workos.fga.resources.create({
resource_type: 'workspace',
external_id: ws.id,
name: ws.name,
parent_resource_id: ws.orgId,
});
// Update your DB with WorkOS resource ID
await db.workspace.update({
where: { id: ws.id },
data: { workosResourceId: resource.id },
});
}
Step 5: Role Assignments
Assign roles to users for specific resources.
Assignment Pattern
// When adding user to workspace with role
await workos.fga.assignments.create({
organization_membership_id: membership.id, // from AuthKit
role_slug: 'workspace-admin', // role defined in dashboard
resource_id: workspace.workosResourceId, // from Step 4
});
Inheritance Behavior
IMPORTANT: If role has child-type permissions, FGA automatically propagates to children.
Example:
- User assigned
workspace-adminonWorkspace:finance - Role includes permission
project:edit - User automatically gets
project:editon ALL projects underWorkspace:finance
Do not manually assign roles to child resources if parent assignment covers it.
Remove Assignment
// When removing user from workspace
await workos.fga.assignments.delete({
organization_membership_id: membership.id,
role_slug: 'workspace-admin',
resource_id: workspace.workosResourceId,
});
Step 6: Access Checks
Check if user has permission before allowing action.
Single Permission Check
// Before allowing project edit
const canEdit = await workos.fga.check({
organization_membership_id: membership.id,
permission: 'project:edit',
resource_id: project.workosResourceId,
});
if (!canEdit.authorized) {
return res.status(403).json({ error: 'Insufficient permissions' });
}
// Proceed with edit
Batch Check Pattern
// Check multiple permissions at once
const [canEdit, canDelete, canShare] = await Promise.all([
workos.fga.check({
organization_membership_id: membership.id,
permission: 'project:edit',
resource_id: project.workosResourceId,
}),
workos.fga.check({
organization_membership_id: membership.id,
permission: 'project:delete',
resource_id: project.workosResourceId,
}),
workos.fga.check({
organization_membership_id: membership.id,
permission: 'project:share',
resource_id: project.workosResourceId,
}),
]);
Step 7: Resource Discovery
Query which resources user can access.
Pattern: List User’s Resources
// "Show all projects this user can edit"
const editableProjects = await workos.fga.organizationMemberships.resources({
organization_membership_id: membership.id,
resource_type: 'project',
permission: 'project:edit',
});
// Returns array of resource objects user can edit
Pattern: List Resource Members
// "Who has access to this workspace?"
const members =
await workos.fga.resources.organizationMemberships({
resource_id: workspace.workosResourceId,
});
// Returns array of memberships with access (direct or inherited)
Pattern: List User’s Roles
// "What roles does this user have?"
const roles = await workos.fga.organizationMemberships.roles({
organization_membership_id: membership.id,
});
// Returns array of role assignments across all resources
Step 8: External ID Lookups
Use your database IDs directly without storing WorkOS IDs.
Pattern: Fetch by External ID
// If you only stored external_id, not workos resource_id
const resource = await workos.fga.organizations.resources.getByExternalId({
organization_id: orgId,
resource_type: 'workspace',
external_id: dbWorkspace.id, // YOUR database ID
});
// Now you have resource.id for assignments/checks
Trade-off: This adds extra API call. Storing both IDs (Step 4) is more efficient.
Verification Checklist (ALL MUST PASS)
# 1. Check FGA is available (not pre-release)
curl -f -H "Authorization: Bearer ${WORKOS_API_KEY}" \
https://api.workos.com/authorization/resources >/dev/null 2>&1 && echo "PASS" || echo "FAIL: FGA not available"
# 2. Check resource types configured in dashboard (manual verification required)
echo "MANUAL: Verify resource types exist in WorkOS Dashboard â Authorization"
# 3. Check resource registration code exists
grep -r "fga.resources.create" . --include="*.ts" --include="*.js" && echo "PASS" || echo "FAIL: No resource registration"
# 4. Check access control implemented
grep -r "fga.check" . --include="*.ts" --include="*.js" && echo "PASS" || echo "FAIL: No access checks"
# 5. Application builds
npm run build && echo "PASS" || echo "FAIL: Build error"
If check #1 fails: FGA is not released yet. Stop implementation until Q1 2026.
If check #2 fails: Configure resource types in Dashboard before writing code. This is a blocking requirement.
Error Recovery
“FGA endpoints return 404”
Root cause: Feature not released yet (scheduled Q1 2026).
Fix: Check docs for release status. Do not implement until available.
“Resource type not found”
Root cause: Resource type slug in code doesn’t match Dashboard config.
Fix:
- Check exact slug in Dashboard â Authorization â Resource Types
- Match slug exactly in
resource_typeparameter (case-sensitive)
“Assignment failed: invalid organization_membership_id”
Root cause: Using user ID instead of organization membership ID.
Fix: Get membership ID from AuthKit:
// WRONG
await workos.fga.assignments.create({
organization_membership_id: user.id, // user ID
});
// CORRECT
const membership = await workos.organizations.memberships.list({
organization_id: orgId,
user_id: user.id,
});
await workos.fga.assignments.create({
organization_membership_id: membership.data[0].id, // membership ID
});
“Permission check always returns false”
Root cause 1: Role doesn’t include permission in Dashboard config.
Fix: Add permission to role in Dashboard â RBAC â Roles.
Root cause 2: No assignment exists for user on resource or ancestor.
Fix: Check assignments exist:
# List user's roles to verify assignment
curl -H "Authorization: Bearer ${WORKOS_API_KEY}" \
"https://api.workos.com/authorization/organization_memberships/${MEMBERSHIP_ID}/roles"
Root cause 3: Using wrong resource ID (your DB ID instead of WorkOS ID).
Fix: Use resource.id from registration response, not external_id.
“Cannot delete resource: has children”
Root cause: Attempting to delete parent resource while children still exist.
Fix: Delete children first, then parent:
// Delete all projects under workspace first
for (const project of projects) {
await workos.fga.resources.delete(project.workosResourceId);
}
// Then delete workspace
await workos.fga.resources.delete(workspace.workosResourceId);
“SDK method not found”
Root cause: SDK version too old or FGA not yet released.
Fix:
# Update SDK to latest
npm install @workos-inc/node@latest
# Verify version supports FGA
npm list @workos-inc/node
Check SDK changelog for FGA support version.
Related Skills
- workos-rbac: Organization-level role-based access control (prerequisite for FGA)
- workos-authkit-nextjs: User authentication and organization management (provides membership IDs)