Restrict access to features in your SaaS application based on subscription tier using FGA policies and relation-based access control.
This guide is also available as an example in the FGA Playground, where you can explore the schema, relationships, and test queries live!
In SaaS applications, it’s common to control access to product features based on a subscription tier. This approach allows product teams to define distinct experiences for different customer segments – like offering basic tools to free users/organizations and premium features to paying ones.
For example, a design tool might offer a Free
tier with limited capabilities and a Pro
tier that unlocks collaboration and team-based workflows.
Use feature entitlements when:
Use this approach when you need dynamic, policy-driven access control for features across different plans or user types. It’s especially helpful in multi-tenant SaaS apps where access logic needs to scale cleanly and stay centralized.
version 0.3 type user type organization relation admin [user] relation member [user] inherit member if relation admin // Tiers are defined by subscription attributes relation pro_subscriber [] inherit pro_subscriber if all_of relation admin // In this example, you must be an admin on the org to get access to pro features policy is_pro_subscriber relation free_subscriber [] inherit free_subscriber if any_of policy is_free_subscriber policy is_pro_subscriber // Pro subscribers can also access free features // Feature access based on subscription tier relation feature_projects [] inherit feature_projects if all_of relation member relation free_subscriber relation feature_teams [] inherit feature_teams if relation pro_subscriber // Teams and Projects demonstrate how you can utilize ReBAC permissions // to control access to features based on org subscription tiers type team relation owner [organization] relation view [] inherit view if relation feature_teams on owner [organization] type project relation owner [organization] relation view [] inherit view if relation feature_projects on owner [organization] // Policies check subscription attributes passed from a third party integration policy is_pro_subscriber(subscription_attrs map) { subscription_attrs.subscription_tier == "pro" } policy is_free_subscriber(subscription_attrs map) { subscription_attrs.subscription_tier == "free" }
Note: Feature access is determined entirely by an organization’s subscription attributes, which are evaluated by policy. This approach enables dynamic, attribute-based access control without manually managing feature grants.
Create a file called schema.txt
with the schema above, and apply it to your FGA environment using the CLI.
workos fga schema apply schema.txt
Create warrants that associate users to organizations and add teams / projects.
curl "https://api.workos.com/fga/v1/warrants" \ -X POST \ -H "Authorization: Bearer sk_example_123456789" \ --data-raw \ '[ { "op": "create", "resource_type": "organization", "resource_id": "acme", "relation": "member", "subject": { "resource_type": "user", "resource_id": "user_2oDscjroNWtzxzYEnEzT9P7VYEe" } }, { "op": "create", "resource_type": "organization", "resource_id": "acme", "relation": "admin", "subject": { "resource_type": "user", "resource_id": "user_3kLwpXyzQTuvbNApRmC5X4ZhAmd" } }, { "op": "create", "resource_type": "team", "resource_id": "team-1", "relation": "owner", "subject": { "resource_type": "organization", "resource_id": "acme" } }, { "op": "create", "resource_type": "project", "resource_id": "project-1", "relation": "owner", "subject": { "resource_type": "organization", "resource_id": "acme" } } ]'
Once everything is set up, check if a user can access specific features.
curl "https://api.workos.com/fga/v1/check" \ -X POST \ -H "Authorization: Bearer sk_example_123456789" \ --data-raw \ '{ "op": "all_of", "checks": [ { "resource_type": "project", "resource_id": "project-1", "relation": "view", "subject": { "resource_type": "user", "resource_id": "user_2oDscjroNWtzxzYEnEzT9P7VYEe" }, "context": { "subscription_attrs": { "subscription_tier": "free" } } }, { "resource_type": "team", "resource_id": "team-1", "relation": "view", "subject": { "resource_type": "user", "resource_id": "user_3kLwpXyzQTuvbNApRmC5X4ZhAmd" }, "context": { "subscription_attrs": { "subscription_tier": "pro" } } } ] }'