You know how on a git repository your permissions feel so intuitive? You're not just a generic 'user' across the whole application. You might be the owner of your own repo, a maintainer with merge rights on a team project, and only have permission to help organize the bug reports and feature requests on an open-source library you contribute to.
This whole system, where your permissions change based on your specific connection to a repo, a pull request, or an organization is the core idea behind Relationship-Based Access Control (ReBAC). It’s an authorization model based on how users work together. Access is defined by your relationship to the resource and not just what role you’re assigned.
ReBAC isn't the only choice for access control. It's one tool in a toolbox that includes other models like RBAC and ABAC. As a developer, you might then ask, 'Which one is the right tool for my job?'"
To help you navigate that choice, we've put together a detailed post on how to choose the right authorization model for your SaaS application.
Understanding How ReBAC Systems Function
A ReBAC system is built on three main concepts that link together, which we'll break down using a git repository as our example.
1. Subjects in a ReBAC System
First up, you have the "Subject." This is the person or service that's trying to do something.
Example: A developer's account, user:jane-doe
.
Example: An automated service, like service-account:git-actions
.
2. Objects: the Resources Controlled by ReBAC
Next, there's the "Object." This is the resource the Subject wants to interact with or change.
Example: A whole repository, like repo:acme-corp/webapp
.
Example: A specific pull request, like pr:123
.
3. Relationships: the Core of ReBAC
Finally, and most importantly, you have the "Relationship." This is the piece that connects a subject to an object. It’s a simple, stored fact that describes their association. This isn't the permission itself, but a label defining the connection.
These relationships come in two main types:
- A direct relationship is a simple, stored fact, like a direct assignment.
Example: user:jane-doe
is a maintainer of repo:acme-corp/webapp
.
- An indirect relationship is where the system connects the dots for you. This is often called inherited permission.
Example: Dave gets write_access
to a repo indirectly simply because he's a member of a team that has that access.
How Does ReBAC Grant Permissions?
So if the relationship isn't the permission, how is a decision actually made? The system uses a separate set of rules that it checks against the facts.
Let's walk through a real git repository scenario:
The Ask: Jane (
user:jane-doe
) tries to merge a pull request into the main branch of theacme-corp/webapp repo
.The System Checks: The system doesn't ask, "Is Jane an admin?" Instead, it asks, "How is Jane connected to this specific repo?"
It Finds a Fact: The system sees the stored fact: (
user:jane-doe
,maintainer
,repo:acme-corp/webapp
).It Checks the Rulebook: The repository's rules say something like: "Anyone with the maintainer relationship is allowed to
merge_pull_request
."The Result: The fact matches the rule, and the merge is allowed.
The power of this model is its flexibility. It means you can tweak your enforcement rules in GitHub (like requiring two reviews before merging) at any time, without having to go back and change the underlying relationship facts for all your developers.
Challenges of Implementing ReBAC in Your Authorization System
Thinking about using ReBAC? Here's a quick heads-up on the common challenges you might run into.
Performance Considerations for Scaling ReBAC
To check one permission, a ReBAC system might have to look up several different relationships that connect together. For example, checking if a user can see a file might involve checking their team memberships, which in turn involves checking the team's parent organization, and so on.
This can sometimes lead to slow queries and performance hits. As your application grows, it takes lots of engineering work to keep those lookups from slowing everything down.
Designing Effective ReBAC Models
What makes ReBAC so great is its flexible, graph-style structure, but that's also what makes it tricky. The complexity isn't in a long list of rules, but in the designing of the relationship model itself.
Figuring out the "right" way to model your permissions (e.g., should a manager automatically inherit the permissions of everyone on their team?) is a big deal to start with. A poorly designed model can be hard to understand and even harder to change later on.
Ensuring Audibility in Complex ReBAC Models
A side effect of a complex model is that auditing permissions can be tough. Answering a seemingly simple question like, "Why does Jane have access to this document?" can feel like untangling a ball of yarn. You might have to trace a path through five different indirect relationships involving teams and groups, which can make security reviews and debugging a pain.
Keeping Your Data in Sync
Another roadblock you could run into is the dual-write problem. If your app's source of truth includes who's on what team or who owns what file lives in your main database and your ReBAC system is a separate service with its own database, it will need its own copy of that data to know what's going on.
And that’s how to get into the dual-write problem. When a manager moves someone to a new team, you've got to update that info in two places: your app's database and the authorization system. So, what happens if the write to your database succeeds, but the one to the auth system fails? Now you've got a user whose status is out of sync with their permissions. That can cause confusing bugs or, even worse, a security risk.
This is a well-known problem in distributed systems If you want to see the different patterns for solving it, check out this blog post on "Handling the Dual-Write Problem in Distributed Systems".
All of these challenges point to the same advice. Using ReBAC successfully means putting in serious thought at the design stage. It trades the ongoing complexity of other systems for a thoughtful investment in your initial model.
How Auth0 FGA Helps with ReBAC
So how do you actually use these concepts in a real tool? Let's look at Auth0 FGA, a managed service for ReBAC. It provides the tools to get hands-on with the concepts we've been talking about and is built on the open-source OpenFGA (inspired by Google's Zanzibar).
Let's model our git scenario to show both direct and indirect permissions.
Defining the ReBAC Authorization Model in Auth0 FGA
First, we define our authorization model in the Auth0 FGA Model Explorer. This is where we'll spell out the concepts we talked about earlier in the blog post. By defining the types of objects to be protected and the possible relations a subject can have with them. For example, a key relation in our model is can_write
, which grants access if a subject has either a direct maintainer
relationship with the object, or an indirect one by being a member
of a writer
team.
model schema 1.1 type user type team relations define member: [user] type repository relations define maintainer: [user] define writer: [team] define can_write: maintainer or member from writer
This model sets up our rule:
- A user gets
can_write
permission directly if they are amaintainer
. - A user gets
can_write
permission indirectly if they are amember
of ateam
that is a designatedwriter
.
Creating Relationship Tuples in Auth0 FGA
Remember how we described a relationship as just a simple, stored fact? Now, it's time to actually store some of those facts in our system.
In the world of Auth0 FGA, each one of these "facts" is stored in something called a Tuple. Think of a Tuple as a single line-item that records one specific relationship, it's the raw data that connects a user to an object and solidifies their connection. You can use Tuple Management in the Auth0 FGA dashboard to add Tuples
[ // 1. Anne is a direct maintainer of the "webapp" repo { "user": "user:anne", "relation": "maintainer", "object": "repository:webapp" }, // 2. The "developers" team is a writer on the "webapp" repo { "user": "team:developers", "relation": "writer", "object": "repository:webapp" }, // 3. Bob is a member of the "developers" team { "user": "user:bob", "relation": "member", "object": "team:developers" } ]
Verifying Access in Auth0 FGA
Now for the test. We ask the system simple questions, and it uses the model and the facts to give us an answer. In Auth0 FGA, we write assertions for this.
Check #1 (Direct Relationship)
- Question: Can Anne write in the "webapp" repo?
- Answer: Yes. The system checks the
can_write
rule. It sees thatmaintainer
is a type ofcan_write
, finds the fact that Anne is amaintainer
, and grants access.
Check #2 (Indirect Relationship)
- Question: Can Bob write in the "webapp" repo?
- Answer: Yes. This is where the power of the model shines.
The system checks:
Is Bob a direct
maintainer
? No.Is Bob a
member
of ateam
that is awriter
on this repo?It finds the fact that the
team:developers
is a writer onrepository:webapp
.It then checks if
user:bob
is amember
ofteam:developers
. Yes, it finds that fact.The indirect rule (member from writer) is satisfied. Access granted.
Bob never got permission directly, but the system figured it out by traversing the relationship graph. If you want to get your hands dirty and build this yourself, the Auth0 FGA documentation is the best place to start.
You can see what each of these steps look like in Developer Mode of the Auth0 FGA playground.
Final Thoughts on Relationship-Based Access Control
To bring it all together, ReBAC is an approach for modeling permissions that mirrors how your application actually works. Instead of relying on broad, static roles, it focuses on specific relationships: who owns a document, who is a member of a team, or who is assigned to a task.
It does need more thought upfront to design that relationship model well. But the payoff for that initial work is an authorization system that's ultimately easier to use and change. When your app's features change, you often end up adding new relationships rather than rewriting hard-coded permission logic.
Auth0 FGA is a tool that provides the high-performance infrastructure for this approach, but the essential first step is always the work of designing a solid model that fits your application's needs.
About the author

Will Johnson
Developer Advocate
Will Johnson is a Developer Advocate at Auth0. He loves teaching topics in a digestible way to empower more developers. He writes blog posts, records screencasts, and writes eBooks to help developers. He's also an instructor on egghead.io.
View profile