A QUICK SUMMARY – FOR THE BUSY ONES
TABLE OF CONTENTS
Managing access to individual resources in a cloud environment can be challenging, especially when handling permissions for multiple users and files. Imagine a scenario where different users need various access levels to several files stored in a cloud drive. Traditional access management solutions often fail to provide the necessary granularity and flexibility.
This is where OpenFGA by the Auth0 team steps in, offering a fine-grained authorization system that enables precise and effective permissions management at an individual resource level.
In this article, we’ll explore how OpenFGA can be implemented to solve common access control challenges and ensure secure, efficient user permissions management.
OpenFGA offers a powerful solution for managing fine-grained access to resources:
Fine-Grained Authorization (FGA) allows granting particular users the right to carry out specific actions on individual resources.
A type is a string that defines a category of objects sharing similar characteristics:
User
Folder
Document
A type definition specifies all potential relationships that a user or another object can have with this type:
type document
relations
define can_view: [user] or owner
define can_edit: [user] or owner
define owner: [user]
Structured way to define who has access to what resources under which conditions:
model
schema 1.1
type document
relations
define can_view: [user] or owner
define can_edit: [user] or owner
define owner: [user]
type user
An object within the system represents an entity, and users' relationships to it are defined using relationship tuples as specified in the authorization model:
document:expenses
folder:general
A user is an entity in the system that can be related to an object:
user:john
document:sales
A relationship tuple comprises a user, relation, and object. These tuples are written and stored within OpenFGA to define specific relationships and permissions within the system.
Assertions are queries to verify if a user has a certain relationship to a resource.
is user:alice related to resource:file1 as can_view? // true
Let’s define some rules and challenges which we would like to solve:
Let’s take a look at a simple cloud drive structure. We can have different users and resources.
To start working on the defined rules above, we must first take care of the authorization model. Just before we start with it, let’s clarify what the model is.
The Authorization Model in OpenFGA is a structured way to define who has access to what resources under which conditions. It is based on ReBAC rules. Key components of the model are types and relations. Let’s jump to defining the model for our needs:
model
## schema version must be defined
schema 1.1
## Let's assume that document will be our resource
type document
relations
define can_view: [user] or owner
define can_edit: [user] or owner
define owner: [user]
### user type must be defined if we are using it in the model
type user
With the model in place, we can now add tuples. Using these, we will identify the owner of the file. In short, tuples are building blocks for defining relations between types.
Let’s take a look at how tuples should be defined based on our needs:
## A tuple consists of object, relation and user fields,
## where 'user' does not mean 'user of the system' but rather a subject.
## Our first tuple defines the relation between the user "John"
## and document created by him "sales"
## user john is related to document sales as owner
Object: "document:sales"
Relation: "owner"
User: "user:john"
## user mark is related to document expenses as owner
Object: "document:expenses"
Relation: "owner"
User: "user:mark"
Great! With our first tuples and defined model, we can now make assertions.
Let's begin with a simple use case:
John tries to view the “sales” document. That’s an easy one. But how would this assertion be structured in OpenFGA?
Is user:john
related to document:sales
as can_view
? ✅
We have a user (subject), object and relation, same as in tuples.
The basic query structure should be read as:
Is user:{userId}
related to object:{objectId}
as a relation?
From a code perspective, the query would be defined as:
Object: "document:expenses"
Relation: "can_view"
User: "user:john"
Great, but we’ve only defined the owner tuple to specify who owns the file.
How do we know if John can view it?
Let’s jump back to the schema. We have defined that the document can be viewed by direct user [user] or owner of the file:
define can_view: [user] or owner
It gives us clear information that a user who owns the file can view it. Also, the owner can edit the file.
Let’s make some more quick assertions:
Is user:mark
related to document:sales
as can_view
? 🚫
That assertion is false cause user Mark is not an owner of the document sales and he has not been given access to it.
How can we fix it? By adding a tuple that adds a relation can_view
between Mark and the file:
### user mark is related to document sales as can_view
Object: "document:sales"
Relation: "can_view"
User: "user:mark"
Now Mark can view document sales. We have defined granular access to that document with the FGA tuple. But still, Mark can’t edit the file because there is no tuple with a can_edit
relation. To fix this we need to add another tuple:
### user Mark is related to document sales as can_edit
Object: "document:sales"
Relation: "can_edit"
User: "user:mark"
Great, now Mark can edit and view the file. But something doesn't quite add up. If someone can edit a file, they should also be able to view it. Do we really need two separate tuples? Let's simplify and revisit the schema:
model
schema 1.1
type document
relations
### define that the document can be viewed by a user with a can_edit relation
define can_view: [user] or owner or can_edit
define can_edit: [user] or owner
define owner: [user]
type user
Now, creating a tuple for a new user with relation can_edit allows them to view a file, too.
What with a situation where every user can view the file? Defining tuples for every single user can be cumbersome. OpenFGA gives us wildcards. Let’s extend our schema with them to make our life easier.
model
schema 1.1
type document
relations
## Extending can_view relation with user:* allow us to make decision
## if a file can be viewed by all of the users
define can_view: [user, user:*] or owner or can_edit
define can_edit: [user] or owner
define owner: [user]
type user
In simple terms, we have defined that all users can view a file, but we need to directly mark a specific file with this setting to make it visible to everyone. For it, we need to create a tuple before defining a tuple where Mark is an owner of the document expenses. Let’s make it public!
Object: "document:expenses"
Relation: "can_view"
User: "user:*"
Great, now every user can view an expense document.
🚨 Before a tuple is saved, it is validated based on the authorization model. The tuple would be rejected if the authorization model had not been extended beforehand and access to every user could not be given.
Now all assertions would pass:
## Can Paul view a document expenses?
is user:paul related to document:expenses as can_view ? // true
### Can Katie view a document expenses?
is user:katie related to document:expenses as can_view ? // true
Currently, we have individual documents. Our goal is to enable folder-level access such that granting a user the can_view
permission for a folder will automatically allow them to view all documents contained within that folder. This approach will also enable users to share entire folders along with all their contents without adding tuples for every single document.
Let’s start with the authorization model:
model
schema 1.1
type document
relations
define can_view: [user, user:*] or owner or can_edit
define can_edit: [user] or owner
define owner: [user]
### Every document now will have the possibility to assign parent (folder)
define parent: [folder]
type user
type folder
relations
### Let's create simple relations for folders
define owner: [user]
define can_view: [user] or owner
Let’s connect a document with a folder and assign parent relation with tuples:
### First define the owner of the folder
Object: "folder:general"
Relation: "owner"
User: "user:paul"
### Let's assign the parent folder to the invoices document
Object: "folder:general"
Relation: "parent"
User: "document:invoices"
We assigned the parent to the invoices document. Let’s share the whole folder with Mike:
### To share the folder with Mike - we need to create a tuple for it
Object: "folder:general"
Relation: "can_view"
User: "user:mike"
What did we do? We gave access to the folder. Mike can view a folder which is the parent of the invoice document. Let’s make an assertion:
Is user:mike
related to document:invoices
as can_view
? 🚫
Unfortunately, Mike still is not able to see the document in the folder, even when he has a can_view
relation to this folder. To make it possible we need to return to our model:
model
schema 1.1
type document
relations
## Adding "can_view from parent" gives information that a document can
## can be viewed by a user with relation can_view to parent (folder).
## In simple words - every user who has can_view permission to a parent folder
## can view documents inside of it (connected with parent relation)
define can_view: [user, user:*] or owner or can_edit or can_view from parent
define can_edit: [user] or owner
define owner: [user]
define parent: [folder]
type user
type folder
relations
## Let's create simple relations for folders
define owner: [user]
define can_view: [user] or owner
We have extended relation can_view
on a document. Thanks to that, now the assertion would pass:
Is user:mike
related to document:invoices
as can_view
? ✅
Implementing a fine-grained authorization system like OpenFGA changes how we manage resource access in various environments. By defining a robust authorization model, utilizing tuples for establishing relationships, and enabling hierarchical access, we can achieve precise control over who can view and edit resources. OpenFGA’s support for flexible permission settings and wildcards further simplifies the management process, ensuring that access control is both secure and easy to handle. With these tools, organizations can confidently provide the appropriate access to their resources, enhancing security and operational efficiency.
Our promise
Every year, Brainhub helps 750,000+ founders, leaders and software engineers make smart tech decisions. We earn that trust by openly sharing our insights based on practical software engineering experience.
Authors
Popular this month