[REPORT] From Vision to Code: A Guide to Aligning Business Strategy with Software Development Goals is published!
GET IT here

Relationship-based access control with OpenFGA

readtime
Last updated on
January 22, 2025

A QUICK SUMMARY – FOR THE BUSY ONES

TABLE OF CONTENTS

Relationship-based access control with OpenFGA

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.

Tl;dr – for the busy ones

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.

Type

A type is a string that defines a category of objects sharing similar characteristics:

  • User
  • Folder
  • Document

Type Definition

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]

Authorization model

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

Object

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

User

A user is an entity in the system that can be related to an object:

  • user:john
  • document:sales

Tuple

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

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

Implementation

Let’s define some rules and challenges which we would like to solve:

  • Every user creating a file is an owner of the resource
  • View / Edit permissions can be given to other users
  • A user can view a file if they have an edit permission
  • Single resources can be shared with all users to view a file - public access
  • A file can be viewed by users that can view a parent folder

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.

Authorization Model

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.

Assertion

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.

Giving access to documents to all users

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

Grouping documents with folders

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? ✅

Conclusion

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.

Frequently Asked Questions

No items found.

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

Przemysław Chudzia
github
JavaScript Software Engineer

He brings over 6 years of experience in web development, specializing in JavaScript frameworks like React & Angular on the frontend and Node.js & Nest.js on the backend. His approach emphasizes creating tailored, high-performance solutions that meet diverse client needs while staying at the forefront of industry trends.

Marek Gryszkiewicz
github
JavaScript Software Engineer

FullStack Developer with expertise in JavaScript, specializing in creating dynamic and interactive web applications. He is also a passionate content creator on Udemy, where he shares his knowledge and expertise with learners worldwide. His courses focus on WebRTC and FullStack Development.

Przemysław Chudzia
github
JavaScript Software Engineer

He brings over 6 years of experience in web development, specializing in JavaScript frameworks like React & Angular on the frontend and Node.js & Nest.js on the backend. His approach emphasizes creating tailored, high-performance solutions that meet diverse client needs while staying at the forefront of industry trends.

Marek Gryszkiewicz
github
JavaScript Software Engineer

FullStack Developer with expertise in JavaScript, specializing in creating dynamic and interactive web applications. He is also a passionate content creator on Udemy, where he shares his knowledge and expertise with learners worldwide. His courses focus on WebRTC and FullStack Development.

previous article in this collection

No items found.

It's the first one.

next article in this collection

It's the last one.