WorkOS Docs Homepage
FGA
API referenceDashboardSign In
Getting StartedOverviewOverviewQuick StartQuick StartPlaygroundPlaygroundKey ConceptsSchemaSchemaWarrantsWarrantsResourcesResourcesPoliciesPoliciesQuery LanguageQuery LanguageWarrant TokensWarrant TokensOperations & UsageOperations & UsageManagementSchema ManagementSchema ManagementLocal DevelopmentLocal DevelopmentIdentity Provider SessionsIdentity Provider SessionsModelingOrg Roles & PermissionsOrg Roles & PermissionsCustom RolesCustom RolesGoogle DocsGoogle DocsEntitlementsEntitlementsUser GroupsUser GroupsManaged Service ProviderManaged Service ProviderAttribute-Based Access ControlAttribute-Based Access ControlConditional RolesConditional RolesPolicy ContextPolicy ContextPublic AccessPublic AccessSuperusersSuperusersBlocklistsBlocklists
API Reference
API Reference
Events
Events
Integrations
Integrations
Migrate to WorkOS
Migrate to WorkOS
SDKs
SDKs

Policy Context

Learn how to pass context to policies in FGA.

On this page

  • When to Use it
  • Check Context
    • 1. Apply the schema
    • 2. Create warrants
    • 3. Check access
  • Injected Context
    • 1. Apply the schema
    • 2. Create warrants
    • 3. Update app code to sync resource metadata
    • 4. Check access

Policies in FGA allow you to define complex access control rules based on the context of the request. This context can include resource attributes, location and temporal data, or any other relevant information that can help determine access to a specific resource.

This guide will walk you through the process of creating a policy that pulls context in two different ways: check context and injected context.

When to Use it

Use policy context when:

  • Dynamic Access: You need to make access decisions based on runtime attributes such as location, time of day, IP address, device type, or authentication method.
  • Resource Attribute-Based Access: You want to enforce permissions based on properties of the resource itself such as a course’s level, a document’s classification, or user data.
  • Complex Policies: You need to evaluate multiple attributes or conditions together to enforce advanced access rules.

Check Context

In this approach, you pass context directly in the check request. These values are made available to the policy as named parameters and must be explicitly defined in the policy function signature within your schema.

version 0.3
type user
type course
relation editor [user]
relation viewer [user]
relation instructor [user]
relation edit []
relation view_materials []
relation moderate_discussion []
inherit edit if
all_of
relation editor
policy can_edit_course
policy has_security_compliance
inherit view_materials if
any_of
all_of
relation viewer
policy can_access_materials
relation edit
inherit moderate_discussion if
all_of
relation instructor
policy can_moderate_discussion
policy can_edit_course(check_data map, user_attr map) {
check_data.resource_type == "course" &&
check_data.resource_id in user_attr.assigned_course_ids &&
user_attr.role == "instructor"
}
policy can_access_materials(user_attr map, course_attr map) {
user_attr.is_enrolled == true &&
user_attr.org_id == course_attr.org_id
}
policy can_moderate_discussion(user_attr map, course_attr map) {
user_attr.verified == true &&
course_attr.discussion_enabled == true &&
course_attr.course_level == "advanced"
}
policy has_security_compliance(security_info map) {
security_info.mfa_enabled == true &&
date(security_info.last_password_change) > now() - duration("90d")
}

1. Apply the schema

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.

workos fga schema apply schema.txt

2. Create warrants

Create warrants that associate users with courses. We’ll make a user an editor of a course:

Create warrants
curl "https://api.workos.com/fga/v1/warrants" \
-X POST \
-H "Authorization: Bearer sk_example_123456789" \
--data-raw \
'[
{
"op": "create",
"resource_type": "course",
"resource_id": "course_1",
"relation": "editor",
"subject": {
"resource_type": "user",
"resource_id": "user_1"
}
}
]'

3. Check access

With our environment setup, we can check the user’s permission to view_materials on a course.

Check if a user has a permission on a course
curl "https://api.workos.com/fga/v1/check" \
-X POST \
-H "Authorization: Bearer sk_example_123456789" \
--data-raw \
'{
"checks": [
{
"resource_type": "course",
"resource_id": "course_1",
"relation": "view_materials",
"subject": {
"resource_type": "user",
"resource_id": "user_1"
},
"context": {
"check_data": {
"resource_type": "course",
"resource_id": "course_1"
},
"user_attr": {
"assigned_course_ids": ["course_1", "course_2"],
"role": "instructor",
"is_enrolled": true,
"org_id": "org_123",
"verified": true
},
"course_attr": {
"org_id": "org_123",
"discussion_enabled": true,
"course_level": "advanced"
},
"security_info": {
"mfa_enabled": true,
"last_password_change": "2025-02-01T00:00:00Z"
}
}
}
]
}'

In this example, the check context includes check_data, user_attr, course_attr, and security_info. The policy will evaluate these attributes to determine if the user has access to view materials for the course.

A drawback with this approach is the size of the context in the check request. This method of passing context requires no state in FGA (other than warrant data), but it starts to break down with complex schemas that require large sets of context data. Consider using injected context for more complex schemas.

Injected Context

In this method, you can use context injected by the FGA service. Use injected context to fetch resource metadata in your policy so that you don’t have to pass it in the check request.

This is useful when your schema requires large context objects, when the context is not known at the time of the check, or when you want to change schemas without updating context in the check request.

Read more about injected context and helper functions in the policy documentation.

version 0.3
type user
type course
relation editor [user]
relation viewer [user]
relation instructor [user]
relation edit []
relation view_materials []
relation moderate_discussion []
inherit edit if
all_of
relation editor
policy can_edit_course
policy has_security_compliance
inherit view_materials if
any_of
all_of
relation viewer
policy can_access_materials
relation edit
inherit moderate_discussion if
all_of
relation instructor
policy can_moderate_discussion
policy can_edit_course() {
let user_metadata = get_metadata(check_ctx.subject_type, check_ctx.subject_id);
let is_user = check_ctx.subject_type == "user";
let is_course = check_ctx.resource_type == "course";
let is_assigned = check_ctx.resource_id in user_metadata.assigned_course_ids;
let is_instructor = user_metadata.role == "instructor";
is_user && is_course && is_assigned && is_instructor
}
policy can_access_materials() {
let user_metadata = get_metadata(check_ctx.subject_type, check_ctx.subject_id);
let course_metadata = get_metadata(check_ctx.resource_type, check_ctx.resource_id);
let is_user = check_ctx.subject_type == "user";
let is_enrolled = user_metadata.is_enrolled == true;
let same_org = user_metadata.org_id == course_metadata.org_id;
is_user && is_enrolled && same_org
}
policy can_moderate_discussion() {
let user_metadata = get_metadata(check_ctx.subject_type, check_ctx.subject_id);
let course_metadata = get_metadata(check_ctx.resource_type, check_ctx.resource_id);
let is_user = check_ctx.subject_type == "user";
let is_verified = user_metadata.verified == true;
let discussion_enabled = course_metadata.discussion_enabled == true;
let is_advanced = course_metadata.course_level == "advanced";
is_user && is_verified && discussion_enabled && is_advanced
}
policy has_security_compliance() {
let user_metadata = get_metadata(check_ctx.subject_type, check_ctx.subject_id);
let is_user = check_ctx.subject_type == "user";
let mfa_enabled = user_metadata.mfa_enabled == true;
let recent_password = date(user_metadata.last_password_change) > now() - duration("90d");
is_user && mfa_enabled && recent_password
}

1. Apply the schema

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.

workos fga schema apply schema.txt

2. Create warrants

Create warrants that associate users with courses. We’ll make a user an editor of a course:

Create warrants
curl "https://api.workos.com/fga/v1/warrants" \
-X POST \
-H "Authorization: Bearer sk_example_123456789" \
--data-raw \
'[
{
"op": "create",
"resource_type": "course",
"resource_id": "course_1",
"relation": "editor",
"subject": {
"resource_type": "user",
"resource_id": "user_1"
}
}
]'

3. Update app code to sync resource metadata

In order to pull resource metadata from our policies, we need to update our app code to sync resource metadata with the FGA service. This is done by calling the update resource endpoint in the FGA API.

Update resource metadata
curl "https://api.workos.com/fga/v1/resources/user/d6ed6474-784e-407e-a1ea-42a91d4c52b9" \
-X PUT \
-H "Authorization: Bearer sk_example_123456789" \
--data-raw \
'{
"meta": {
"assigned_course_ids": ["course_1", "course_2"],
"role": "instructor",
"is_enrolled": true,
"org_id": "org_123",
"verified": true
"mfa_enabled": true,
"last_password_change": "2025-02-01T00:00:00Z"
}
}'

4. Check access

With our environment setup, we can check the user’s permission to view_materials on a course.

Check if a user has a permission on a course
curl "https://api.workos.com/fga/v1/check" \
-X POST \
-H "Authorization: Bearer sk_example_123456789" \
--data-raw \
'{
"checks": [
{
"resource_type": "course",
"resource_id": "course_1",
"relation": "view_materials",
"subject": {
"resource_type": "user",
"resource_id": "user_1"
},
"context": {}
}
]
}'
© WorkOS, Inc.
FeaturesAuthKitSingle Sign-OnDirectory SyncAdmin PortalFine-Grained Authorization
DevelopersDocumentationChangelogAPI Status
ResourcesBlogPodcastPricingSecuritySupport
CompanyAboutCustomersCareersLegalPrivacy
© WorkOS, Inc.