Learn how to pass context to policies in FGA.
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.
Use policy context when:
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") }
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
Create warrants that associate users with courses. We’ll make a user an editor of a course:
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" } } ]'
With our environment setup, we can check the user’s permission to view_materials
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.
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 }
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
Create warrants that associate users with courses. We’ll make a user an editor of a course:
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" } } ]'
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.
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" } }'
With our environment setup, we can check the user’s permission to view_materials
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": {} } ] }'