Build a Google Docs-like authorization model in which users have varying levels of access to documents they can collaborate on with each other.
If you are building an application that supports user-generated content (e.g. documents, notes, images, etc.), chances are you’ll need to build complex authorization logic to allow users to share access to that content with others. Building this type of fine-grained authorization logic can get complicated fast and is difficult to scale.
In this guide, we’ll use FGA to implement an authorization model similar to the Google Docs document sharing model. It will allow users to grant read and/or write access to other users on individual documents and folders that contain other documents and folders nested inside of them.
To get the most out of this guide, you’ll need:
Don't see an SDK you need? Contact us to request an SDK!
Since documents are the main type of resource we’re dealing with in our application, schema will include a document
resource type. We’ll also have a user
resource type to represent our users.
Here are the requirements for our authorization model:
owner
, editor
, and viewer
To model our authorization requirements such that we can answer these questions, let’s define the following schema:
version 0.2 type user type document // Note: folders are modeled as documents. // They can be parents of other documents. relation parent [document] relation role_owner [user] relation role_editor [user] relation role_viewer [user] relation permission_users_write [] relation permission_users_read [] relation permission_content_write [] relation permission_content_read [] inherit role_owner if relation role_owner on parent [document] inherit role_editor if any_of relation role_owner relation role_editor on parent [document] inherit role_viewer if any_of relation role_editor relation role_viewer on parent [document] inherit permission_users_write if relation role_owner inherit permission_users_read if any_of relation role_viewer relation permission_users_write inherit permission_content_write if relation role_editor inherit permission_content_read if any_of relation role_viewer relation permission_content_write
With the schema above, we’ve implemented an authorization model that can easily answer the following questions:
U
have permission P
on document D
?U
have permission P
on?P
on document D
?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
Define a resource type schema in the FGA dashboard using the schema editor available on the Schema page.
Now that we have defined a resource type schema that meets our authorization requirements, we need to create warrants that associate:
owner
, editor
, or viewer
)Let’s create a few warrants between documents doc-1
, doc-2
, doc-3
, folder-1
, folder-f2
, 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": "document", "resource_id": "doc-1", "relation": "parent", "subject": { "resource_type": "document", "resource_id": "folder-1" } }, { "op": "create", "resource_type": "document", "resource_id": "doc-2", "relation": "parent", "subject": { "resource_type": "document", "resource_id": "folder-1" } }, { "op": "create", "resource_type": "document", "resource_id": "doc-3", "relation": "parent", "subject": { "resource_type": "document", "resource_id": "folder-2" } }, { "op": "create", "resource_type": "document", "resource_id": "folder-1", "relation": "parent", "subject": { "resource_type": "document", "resource_id": "folder-2" } }, { "op": "create", "resource_type": "document", "resource_id": "folder-1", "relation": "role_owner", "subject": { "resource_type": "user", "resource_id": "user_2oDscjroNWtzxzYEnEzT9P7VYEe" } }, { "op": "create", "resource_type": "document", "resource_id": "folder-2", "relation": "role_viewer", "subject": { "resource_type": "user", "resource_id": "user_2oDscjroNWtzxzYEnEzT9P7VYEe" } }, ]'
With our document hierarchy setup and permissions assigned to a user, we can check or query the user’s permissions to enforce access.
curl "https://api.workos.com/fga/v1/check" \ -X POST \ -H "Authorization: Bearer sk_example_123456789" \ --data-raw \ '{ "checks": [ { "resource_type": "document", "resource_id": "doc-1", "relation": "permission_content_read", "subject": { "resource_type": "user", "resource_id": "user_2oDscjroNWtzxzYEnEzT9P7VYEe" } } ] }'
curl "https://api.workos.com/fga/v1/query?q=select%20document%20where%20user:user_2oDscjroNWtzxzYEnEzT9P7VYEe%20is%20permission_content_read" \ -X GET \ -H "Authorization: Bearer sk_example_123456789"
In this guide, we explained how to use FGA to set up an authorization model for shareable content. By giving each user an owner
, editor
, or viewer
role on specific documents, and organizing documents with a parent relationship, we can enforce complex authorization rules. This approach manages fine-grained permissions without the usual complexity.