OpenFGA: An exellent tool for access control authorization
OpenFGA is a robust authorization solution with fast and flexible processing capabilities. In this article, we uncover OpenFGA and its strengths when being built on the relationship-based access control (ReBAC) model.
Bài viết này có phiên bản Tiếng Việt
ReBAC
In digital security, authorization models play an important role in determining "who" has "what rights" over "which resources". Traditional models such as Role-based Access Control (RBAC) and Attribute-based Access Control (ABAC) have been popular solutions for many years. However, as the complexity and scale of systems increase, they cause a greater need for flexibility and granularity. In this context, a new access authorization called Relationship-Based Access Control (ReBAC) has emerged.
Authorization models
ReBAC is an authorization model that considers the relationship between subjects and objects when determining access rights. Unlike the "attribute" in ABAC or "role" in RBAC, the ReBAC model focuses on the "relationship" of users within an organization, society, hierarchy, in addition to common relationships between users and resources. For example, in the case of granting managers access to the data of their subordinates within their group, or allowing project members to access resources related to the project, ReBAC can provide a flexible access control method that better reflects the relational structure in the real world and helps organizations enforce effective security policies.
Google Zanzibar is a prime example of ReBAC. Zanzibar is a system used by Google to handle access control for hundreds of services and products such as YouTube, Drive, Calendar, Cloud, Maps, etc. With the ability to handle complex access control policies at a global scale, Zanzibar is capable of processing billions of queries per second.
OpenFGA
Being inspired by Zanzibar, OpenFGA is introduced as a powerful yet user-friendly access control model for engineers. OpenFGA provides fast and flexible processing capabilities, making it suitable for projects of any scale.
Fine-Grained Authorization (FGA) refers to the ability to specify access rights to specific resources or objects in the system for each user. Fine-Grained Authorization is particularly suitable for large-scale systems with millions of objects, users, and relationships whose access rights are frequently updated. An example of a system using Fine-Grained Authorization is Google Drive, where access rights often change when sharing documents or folders.
OpenFGA addresses access control checks by considering the relationship between the requesting user and the object. The check relies on the system's authorization model and the relationship tuples available at the time to make decisions.
Some concepts in OpenFGA:
Some concepts in OpenFGA
- Store: Each Store contains one or more versions of the Authorization Model and Relationship Tuples. When performing access checks, the StoreID needs to be provided to specify the store containing the model and tuples to be checked. Data in one store cannot be shared with other stores.
- Authorization model:
OpenFGA introduces two syntaxes for defining authorization models: JSON and DSL. Here is an example of an authorization model defined using OpenFGA DSL:
model
schema 1.1
type document
relations
define viewer: [domain#member,user]
define commenter: [domain#member,user]
define editor: [domain#member,user]
define owner: [domain#member,user]
type domain
relations
define member: [user]
type user
Each authorization model is defined by:
-
Type: Defines a set of similar varieties (e.g., document, repository, user, admin, etc.).
-
Relations: Defines the relationships between users and objects.
-
User-to-object
type document relations define viewer: [user:hungtgq1]
-
Object-to-object
type document relations define editor: [application:myapp]a
-
Userset-to-object
type document relations define editor: [organization:myteam#member]
-
Everyone-to-object
type document relations define editor: [user:*]
-
-
Relationship Tuple: A relationship tuple includes the users, relations, and objects stored in OpenFGA. The collection of Relationship Tuples represents the relationships between specific individuals and objects in the system and serves as the basis for OpenFGA's authentication process.
[ { "user": "user:hungtgq1", "relation": "owner", "object": "board:myfirstboard", }, ]
OpenFGA Playground
OpenFGA provides a Playground to allow users to familiarize themselves with ReBAC and OpenFGA.
With the Playground, you can define an authorization using DSL and copy the corresponding JSON version to use in your source code. Currently, the OpenFGA server only supports models in JSON format. To use DSL, you need to use a parser or convert it through the playground.
OpenFGA Playground
Users can access the Playground at OpenFGA Playground or use the integrated Playground in a self-deployed server. We will explore the process of installing OpenFGA through the next demo.
Demo
Installing OpenFGA
OpenFGA supports quick and easy installation using Docker and Kubernetes. You can see more information in the Setup OpenFGA guide. In this article, we will use the provided Docker Compose template to install OpenFGA.
After running the Docker Compose as instructed, you can check the result by accessing the /healthz
endpoint.
OpenFGA installation result
Problem Description
The Architecture Diagram Platform is a platform that allows users to create architectural diagrams and share them with other users. The diagrams can be organized into folders. Only the creator of a diagram has permission to edit and share it, while shared users can only view the diagrams.
With the given problem, we can identify the following objects and relationships:
-
User
-
Board
- Owner
- Viewer
- Parent (parent folder)
- can_view (view permission of the creator and shared users)
- can_share (share permission of the creator)
- can_edit (edit permission of the creator)
-
Folder
- Owner
- can_create (board create permission of the folder's creator)
The corresponding authorization model is as follows:
Authorization model of Architecture Diagram Platform problem
go
model
schema 1.1
type user
type folder
relations
define can_create: owner
define owner: [user]
type board
relations
define can_view: viewer or owner
define can_share: owner
define can_edit: owner
define owner: [user]
define parent: [folder]
define viewer: [user]
Implementation
Currently, OpenFGA provides SDKs for Node.js, Go, .NET, and Python. In this demo, we will use Go. To install the Golang SDK, follow the instructions in the Install SDK Client guide.
We will perform the following steps: Create Store → Add Authorization Model → Add Relationship Tuples → Perform access checks.
- Create Store
To create a Store, we need to create a configuration object containing ApiScheme and ApiHost pointing to the local OpenFGA server as follows:
configuration, err := openfga.NewConfiguration(openfga.Configuration{
ApiScheme: os.Getenv("FGA_API_SCHEME"), // "http"
ApiHost: os.Getenv("FGA_API_HOST"), // "localhost:8080"
})
Then, create the Store using the CreateStore function:
apiClient := openfga.NewAPIClient(configuration)
resp, _, err := apiClient.OpenFgaApi.CreateStore(context.Background()).Body(
openfga.CreateStoreRequest{
Name: "Architecture Diagram Platform",
}).Execute()
When executed successfully, a new Store is created, as shown in the picture below:
We will record the id to use as the Store Id (FGA_STORE_ID) in the following steps.
- Add Authorization Model
To add an Authorization Model, we need to configure the StoreId with the value obtained from the previous step and add it to the configuration object:
StoreId: os.Getenv("FGA_STORE_ID"),
We will use the playground to convert the model to JSON and use it in the source code as follows:
var writeAuthorizationModelRequestString = "{\"type_definitions\":[{\"type\":\"user\",\"relations\":{}},{\"type\":\"folder\",\"relations\":{\"can_create\":{\"computedUserset\":{\"object\":\"\",\"relation\":\"owner\"}},\"owner\":{\"this\":{}}},\"metadata\":{\"relations\":{\"can_create\":{\"directly_related_user_types\":[]},\"owner\":{\"directly_related_user_types\":[{\"type\":\"user\"}]}}}},{\"type\":\"board\",\"relations\":{\"can_view\":{\"union\":{\"child\":[{\"computedUserset\":{\"object\":\"\",\"relation\":\"viewer\"}},{\"computedUserset\":{\"object\":\"\",\"relation\":\"owner\"}}]}},\"can_share\":{\"computedUserset\":{\"object\":\"\",\"relation\":\"owner\"}},\"can_edit\":{\"computedUserset\":{\"object\":\"\",\"relation\":\"owner\"}},\"owner\":{\"this\":{}},\"parent\":{\"this\":{}},\"viewer\":{\"this\":{}}},\"metadata\":{\"relations\":{\"can_view\":{\"directly_related_user_types\":[]},\"can_share\":{\"directly_related_user_types\":[]},\"can_edit\":{\"directly_related_user_types\":[]},\"owner\":{\"directly_related_user_types\":[{\"type\":\"user\"}]},\"parent\":{\"directly_related_user_types\":[{\"type\":\"folder\"}]},\"viewer\":{\"directly_related_user_types\":[{\"type\":\"user\"}]}}}}],\"schema_version\":\"1.1\"}"
var body openfga.WriteAuthorizationModelRequest
if err := json.Unmarshal([]byte(writeAuthorizationModelRequestString), &body); err != nil {
// ...
}
data, response, err := apiClient.OpenFgaApi.WriteAuthorizationModel(context.Background()).Body(body).Execute()
...
When executed successfully, a new Authorization Model is created as shown in the picture below:
We will record the authorization_model_id to use as the Authorization Model Id (FGA_AUTH_MODEL_ID) in the following steps.
- Add Relationship Tuples
To add Relationship Tuples, we use the following code:
body := openfga.WriteRequest{
Writes: &openfga.TupleKeys{
TupleKeys: []openfga.TupleKey{
{
User: openfga.PtrString("user:hungtgq1"),
Relation: openfga.PtrString("owner"),
Object: openfga.PtrString("folder:myfirstfolder"),
},
},
},
AuthorizationModelId: openfga.PtrString(os.Getenv("FGA_AUTH_MODEL_ID"))}
_, response, err := apiClient.OpenFgaApi.Write(context.Background()).Body(body).Execute()
The result is as shown in the picture below:
In this step, we will add the following tuples:
- User "hungtgq1" creates a folder "myfirstfolder"
- User "hungtgq1" creates a board "myfirstboard" inside "myfirstfolder"
- User "hungtgq1" shares the board with user "manlm1"
After adding the tuples, we have the following data in the OpenFGA database like this:
- Perform Access Checks
To perform access checks, we use the following code:
body := openfga.CheckRequest{
AuthorizationModelId: openfga.PtrString(os.Getenv("FGA_AUTH_MODEL_ID")),
TupleKey: openfga.TupleKey{
User: openfga.PtrString("user:hungtgq1"),
Relation: openfga.PtrString("can_view"),
Object: openfga.PtrString("board:myfirstboard"),
},
}
data, _, err := apiClient.OpenFgaApi.Check(context.Background()).Body(body).Execute()
// Print for demo
fmt.Println(*data.Allowed)
We will get a result of either True or False. In the above code, the user "hungtgq1" is the owner of the board "myfirstboard," so he has the "can_view" permission as shown in the picture below:
Similarly, when performing the following checks:
- User "manlm1" can_view permission: True
- User "manlm1" can_edit permission: False
- User "hungtgq1" can_share permission: True
Considerations when using OpenFGA
OpenFGA provides some best practices for deploying it in a production environment. Some notable points are as follows:
Regarding implementation:
- Avoid storing sensitive data or personal information in Relationship Tuples.
- Always declare the Authorization Model ID in requests to improve OpenFGA's processing speed and ensure system consistency during model changes (OpenFGA defaults to finding the latest model if the Model ID is not specified).
Regarding deployment:
-
Configure Authentication: Besides no-authentication (default), you can configure OpenFGA to use authentication methods such as:
- Pre-shared Key Authentication: Use a secret key and set the "Authorization" header in requests to OpenFGA.
- OIDC: Configure the issuer and audience from the provider.
Regarding the database:
- Use a separate database that is not shared with other applications to allow independent scaling and avoid conflicts with other application databases.
- The database should be set up and managed using the "openfga migrate" tool to ensure necessary indexes for any version of OpenFGA
Conclusion
OpenFGA has emerged as a powerful access control solution, driving the concept of Relationship-Based Access Control. Deploying OpenFGA allows systems to scale well with low latency, making it a reliable choice for complex systems. OpenFGA has notable strengths in managing relationships between objects to provide flexible and adaptable access control in modern applications.