In today’s dynamic digital landscape, ensuring users have the right permissions for specific resources is a critical task for any organization. Resource-based access control often needs to be highly fine-grained, especially in complex systems with millions of objects—such as files, projects, records, accounts, etc.
Yet building fine-grained authorization (FGA) correctly with traditional Role-Based Access Control (RBAC) presents several challenges, particularly when it comes to protecting against the top vulnerability in web applications: Broken Access Control.
RBAC itself isn't inherently problematic. But when you try to build fine-grained authorization on top of RBAC, the struggle creeps in. However, this is where Relationship-Based Access Control (ReBAC) shines: evaluating permissions on a per-resource basis. ReBAC makes it simple to perform an authorization check on complex permission models.
The Difficulty of Building Fine-Grained Authorization with RBAC
RBAC has been the go-to method for managing access control in many applications. In RBAC, users are assigned to roles (e.g., admin, editor, viewer), and roles are mapped to permissions that give access to various resources.
A common approach to evaluating access to a resource in an application is to include the permissions in a user’s access token as scopes. However, since most scopes are generic and binary (e.g. _view_all_documents_
), it would be difficult to manage detailed, resource-specific scopes (e.g. viewdocument123) by adding them to the token because it would lead to token bloat. Additionally, since scopes in a token are static by design, they cannot easily reflect real-time permission updates until the token is refreshed.
Because of this, developers will often implement their own custom FGA solutions using RBAC built with SQL tables to unlock more dynamic and real-time access control. But this approach isn’t without its issues.
As systems grow and product improvements require more intricate resource hierarchies, the need to manage numerous roles, users, and resource-specific permissions leads to messy databases and unruly SQL queries, which often involve multiple joins spread out across database tables or services.
In contrast, (ReBAC) models permissions based on relationships between users and resources, simplifying the evaluation logic. With ReBAC, permissions are dynamic and context aware. While you can fully implement RBAC roles within ReBAC, it also allows for more granular control over who can do what with each resource.
Evaluating Resource-Specific Permissions with RBAC
Let’s consider an example: In a project management application, Aria is the admin for her entire organization. She is thus granted permissions to view and edit all documents in any project. The developers of the application have chosen to implement resource-specific authorization by building RBAC in SQL tables.
How do we determine if Aria has access to view a specific document? She would not have any direct assignments because she is not the document creator, but she should inherit the “editor” permissions since she is the organization’s admin. Evaluating this resource-specific authorization check in RBAC would require joining the various database tables that contain the role assignments and resource hierarchies in the organization to see if her "admin" role cascades down to that specific document.
As a result, with each permission check requiring more complex SQL queries, the application’s authorization logic becomes difficult to manage and even harder to update, possibly even leading to performance issues with deeply nested resources.
How ReBAC Simplifies Resource-Specific Permission Checks
With ReBAC, permissions are calculated based on relationships instead of explicitly assigned roles, simplifying the evaluation of who has access or not. In a ReBAC system, Aria’s permissions aren’t solely based on her role but also on her role’s relationships with a document. Since Aria is the admin of the organization, the relationship can be cascaded down from the organization level via inheritance to a specific project or document level.
When you need to make an authorization check on a specific resource, the authorization system references the logic defined in the authorization model against the known object relationships and returns a decision (i.e. true or false). “Can Aria edit the document business-report-1?” = TRUE. We’ll learn later why this authorization check can be so simple.
ReBAC makes it far more efficient than RBAC to check access on a per-resource basis.
Evaluating Permissions with Okta Fine Grained Authorization
Now, let’s explore how Okta Fine Grained Authorization, a hosted and managed service of the popular open-source ReBAC project OpenFGA, leverages relationship-based access control to simplify access management.
Okta Fine Grained Authorization is designed to be flexible enough to work for small startups scaling single applications but powerful enough for large enterprise corporations creating complex relationships at a global scale. It does all of this while staying friendly enough for adjacent stakeholders on the team to read and understand the authorization logic.
Let’s build on the practical example above of Aria being an admin in her project management company. Suppose you have a hierarchical system in which different teams have access to various documents in a project based on the user's assignment.
Sample Authorization Model in Okta Fine Grained Authorization
model
schema 1.1
type user
type organization
relations
define admin: [user]
type project
relations
define organization: [organization]
define owner: [user] or admin from organization
define member: [user]
define viewer: member or owner
type folder
relations
define parent: [folder, project]
define owner: owner from parent
define viewer: viewer from parent or owner
type document
relations
define parent: [folder]
define editor: [user] or owner from parent
define viewer: [user] or viewer from parent or editor
In this model, there are five core “types”: user
, organization
, project
, folder
, and document
. Each plays a specific role in managing access within the system.
User
represents the individuals who will interact with the system. Users are assigned roles and permissions either directly or through group memberships.Organization
contains all the projects with the ability to assign a global admin (like Aria).Project
contains members, controlling access to project documents through membership at a top-level project.Folder
adds a hierarchy to managing resources, connecting users to documents, even through deeply nested folders.Document
inherits access permissions from their parent folder, which ultimately inherits from the project, all while allowing direct assignment to enable use cases like sharing a document with an external user.
A typical authorization scenario might look like this:
- Document business-report-1 is nested inside two Folders under Project Alpha.
- Project Alpha is a part of the Acme Organization.
- Aria is assigned as the admin of the Acme Organization.
In Okta Fine Grained Authorization, these would be represented as relationship tuples stored in the centralized authorization system. You can think of Okta Fine Grained Authorization as a permissions database and tuples are the data. Each tuple will contain a user, relation, and object value like this:
{
User: user:aria
Relation: admin
Object: organization:acme
}
Thus, following the logic in the authorization model, the access check for “Can Aria edit the document business-report-1?” would return TRUE because:
- Aria has the admin relationship in the Acme organization.
- The admin relationship receives the owner relationship in the projects of that organization.
- The owner relationship is thus inherited by the folders in that project.
- Owner permissions continue to cascade down to the editor relationship of the documents inside the organization’s project folder, of which Aria is the admin.
The Okta Fine Grained Authorization API
With ReBAC and Okta Fine Grained Authorization, querying permissions is as simple as checking a user’s relationship to a resource. Instead of performing multiple joins across tables as in RBAC, a single API call checks the relationship, making it highly efficient.
curl -X POST https://api.fga.dev/stores/{store_id}/check \
-H "Authorization: Bearer {your_api_token}" \
-d '{
"tuple_key": {
"user": "user:aria",
"relation": "editor",
"object": "document:business-report-1"
}
}'
While RBAC is a powerful model for role-based permissions, it can struggle when adapted to manage fine-grained, resource-specific permission scenarios. Alternatively, ReBAC excels in dynamic, heavily-nested environments where permissions are based on inherited relationships between users and resources. By adopting ReBAC (and using a platform like Okta Fine Grained Authorization), development teams can streamline how they handle complex access control in their applications, reducing the major risk of broken access control that can stem from complex database queries. Try Okta Fine Grained Authorization for free.