Combine relationship-based access control (ReBAC) with attribute-based access control (ABAC) to create conditional roles.
This guide is now available as an example in the FGA Playground, where you can interact with the schema, warrants, and queries in real-time!
Use FGA to combine Relationship-Based Access Control (ReBAC) and Attribute-Based Access Control (ABAC). Define roles that are bound to specific resources and change based on specific conditions. This allows for more granular control over who can do what, when, and under which circumstances.
Use conditional roles when you cannot determine access by relationships alone. For example, a team member may be allowed to approve some expenses, but only if they are below a certain amount or belong to specific cost centers. As systems grow in complexity, pure ReBAC or ABAC models may become limiting. Conditional roles help bridge that gap with clear, composable rules.
version 0.3 type user type team relation finance_admin [user] relation finance_manager [user] inherit finance_manager if relation finance_admin type expense relation approval_team [team] relation submitter [user] relation approve [] inherit approve if any_of all_of relation finance_manager on approval_team [team] policy can_approve_amount all_of relation finance_admin on approval_team [team] policy is_high_value_expense policy can_approve_amount(expense_attributes map, user_attributes map) { let can_approve_cost_center = expense_attributes.cost_center in user_attributes.approved_cost_centers; let can_approve_amount = expense_attributes.amount <= 1000; can_approve_cost_center && can_approve_amount } policy is_high_value_expense(expense_attributes map) { expense_attributes.amount > 1000 }
Create a file called schema.txt
containing the schema definition from above. Then use the CLI to apply this schema to your WorkOS FGA environment.
Note: make sure to select the correct environment with the CLI
workos fga schema apply schema.txt
Create warrants that associate users, teams, and expenses. The example schema defines the following relationships:
finance_admin
or finance_manager
roles)approval_team
relation)Let’s create a few warrants between team finance-1
, expense expense-1
, and user user_2oDscjroNWtzxzYEnEzT9P7VYEe
:
curl "https://api.workos.com/fga/v1/warrants" \ -X POST \ -H "Authorization: Bearer sk_example_123456789" \ --data-raw \ '[ { "op": "create", "resource_type": "expense", "resource_id": "expense-1", "relation": "approval_team", "subject": { "resource_type": "team", "resource_id": "team-1" } }, { "op": "create", "resource_type": "team", "resource_id": "team-1", "relation": "finance_manager", "subject": { "resource_type": "user", "resource_id": "user_2oDscjroNWtzxzYEnEzT9P7VYEe" } } ]'
With our environment setup, we can check the user’s permission to approve expenses.
curl "https://api.workos.com/fga/v1/check" \ -X POST \ -H "Authorization: Bearer sk_example_123456789" \ --data-raw \ '{ "checks": [ { "resource_type": "expense", "resource_id": "expense-1", "relation": "approve", "subject": { "resource_type": "user", "resource_id": "user_2oDscjroNWtzxzYEnEzT9P7VYEe" }, "context": { "user_attributes": { "user_id": "user_2oDscjroNWtzxzYEnEzT9P7VYEe", "approved_cost_centers": [ "cost-center-1", "cost-center-2" ] }, "expense_attributes": { "cost_center": "cost-center-1", "amount": 150, "description": "New keyboard", "date": "2023-10-01" } } } ], }'