Tekko

Language

Get in Touch

Usually respond within 24 hours

Back to BlogArchitecture

Scaling Permissions with ReBAC: A Guide to OpenFGA and Zanzibar

7 min read
ReBACOpenFGAZanzibarMicroservicesSecurity
Scaling Permissions with ReBAC: A Guide to OpenFGA and Zanzibar

Most developers start their authorization journey with Role-Based Access Control (RBAC). It’s simple, intuitive, and works perfectly for basic applications: an admin can do everything, a viewer can only read. But as systems evolve into distributed microservices and collaborative platforms, RBAC quickly hits a ceiling.

When your requirements shift from "Admins can edit any document" to "Users can edit a document if they own it, or if they belong to a team that has editor access to the folder containing that document," you've entered the realm of Relationship-Based Access Control (ReBAC). Managing this logic inside application code or via complex SQL joins is a recipe for security vulnerabilities and performance bottlenecks.

This article explores how to solve these challenges using the Zanzibar model and OpenFGA, an open-source implementation that brings Google-scale authorization to the rest of the industry.

The Problem: The "Permission Spaghetti" of RBAC and ABAC

In a traditional RBAC system, permissions are usually tied to identity. You check a user's roles at the API gateway or within a middleware. However, RBAC is inherently resource-agnostic. It doesn't handle the relationship between a specific user and a specific resource well.

To solve this, many teams turn to Attribute-Based Access Control (ABAC), where you evaluate policies based on attributes (e.g., document.isPrivate == false). While flexible, ABAC often leads to "Policy Hell," where logic is scattered across various services, making it nearly impossible to audit who has access to what across the entire system.

ReBAC takes a different approach. It treats authorization as a graph problem. Access isn't determined by who you are, but by how you are related to the data.

Understanding the Zanzibar Model

In 2019, Google published a whitepaper on Zanzibar, the internal system they use to manage permissions across Drive, YouTube, and Cloud. Zanzibar was designed to meet three impossible-sounding goals:

  1. Correctness: It must be the single source of truth.
  2. Low Latency: Authorization checks are in the critical path of every request.
  3. High Availability: If the permission system is down, the whole product is down.

At its core, Zanzibar stores tuples. A tuple is a simple assertion of a relationship: user:X has relation:Y on object:Z.

Example: document:budget_2024#viewer@user:alice
This translates to: "User Alice is a viewer of Document: Budget_2024."

By chaining these tuples together, you can create complex, transitive permission structures. If Alice is in the "Finance" group, and the "Finance" group has access to the "Accounting" folder, Alice automatically gains access to everything in that folder. Zanzibar (and OpenFGA) traverses this graph to answer the question: "Can Alice view this file?"

Enter OpenFGA: Zanzibar for the Modern Developer

OpenFGA (Fine-Grained Authorization) is a CNCF Sandbox project originally created by Auth0. It implements the Zanzibar concepts but makes them accessible through a developer-friendly SDK and a Domain Specific Language (DSL) for modeling.

Unlike Google’s internal Zanzibar, which requires Spanner and massive infrastructure, OpenFGA is lightweight and can run backed by PostgreSQL or MySQL. It decouples authorization logic from your application code, providing a centralized "Source of Truth" for permissions.

Modeling Your Domain with OpenFGA DSL

One of the most powerful features of OpenFGA is its DSL. It allows you to define your authorization model in a way that reads like a specification. Let’s look at a real-world example: a Project Management SaaS.

model schema 1.1 type user type organization relations define admin: [user] define member: [user] type project relations define parent_org: [organization] define owner: [user] define contributor: [user] or owner define viewer: [user] or contributor or member from parent_org type document relations define parent_project: [project] define can_view: [user] or viewer from parent_project define can_edit: [user] or contributor from parent_project

In this model:

  • Transitive Access: A user can view a document if they are a viewer of the parent_project.
  • Inheritance: A viewer of a project includes anyone who is a member of the parent_org.
  • Hierarchy: You don't need to manually grant Alice access to 1,000 documents. You simply relate the documents to a project, and Alice to the project.

Implementing ReBAC in Your Stack

Integrating OpenFGA involves three main steps: defining the model, writing relationship tuples, and performing checks.

1. Writing Tuples (The "Write" API)

When a user creates a project in your application, your backend should notify OpenFGA:

await fgaClient.write({ writes: [{ user: 'user:anne', relation: 'owner', object: 'project:marketing_campaign' }] });

2. The Check (The "Check" API)

When Anne tries to edit a document within that project, your middleware calls the Check API:

const { allowed } = await fgaClient.check({ user: 'user:anne', relation: 'can_edit', object: 'document:q4_plan' }); if (!allowed) throw new ForbiddenError();

3. Handling Contextual Data

Sometimes access depends on dynamic data, like "Is the user on the corporate network?" or "Is it during business hours?". OpenFGA supports Contextual Tuples, which are temporary relationships that exist only for the duration of a single check. This bridges the gap between ReBAC and ABAC.

Solving the Performance and Consistency Trade-off

In distributed systems, the biggest challenge for centralized authorization is the "New World Interpretation" problem. If Alice is removed from a team, that change must be reflected globally and immediately to prevent unauthorized access.

Zanzibar (and OpenFGA) solves this using Consistency Tokens (often called zookies in the original paper). When you write a change to OpenFGA, it returns a token representing that point in time. If your application sends that token back during a check call, OpenFGA ensures the check is evaluated against a snapshot of the database that includes that change.

This allows for high-performance caching. You can cache "Check" results safely because you know exactly which version of the authorization graph you are querying.

Real-World Use Case: The Multi-Tenant SaaS

Imagine a B2B SaaS where companies sign up and manage their own users.

  • Requirement: A "Support Engineer" from your company needs access to a client's data, but only if the client has explicitly enabled "Support Access" in their settings.
  • The ReBAC Solution:
    1. Define a relationship support_enabled on the organization type.
    2. Define a relation can_access on the organization that requires (member@your_org AND support_enabled).
    3. When the client toggles the switch, you write/delete the support_enabled tuple.

This logic lives entirely in the OpenFGA model. Your application code doesn't need to know the complex rules of support access; it just asks: check(user:staff_bob, can_access, org:client_acme).

Why This Scales Better Than Traditional Methods

  1. Decoupling: Your microservices don't need to know your permission logic. They just need to know how to call an API.
  2. Auditability: You can query the graph to see every path that leads to a user having access to a resource. This is a nightmare in RBAC/ABAC but a native feature in OpenFGA (via the ListObjects or Expand APIs).
  3. Centralized Management: Changing a business rule (e.g., "Managers now automatically have view access to all sub-projects") requires updating a single line in the DSL, not refactoring ten different services.

Conclusion: Making the Shift

Implementing ReBAC via OpenFGA is a strategic investment in your system's architecture. While it requires a shift in how you think about permissions—moving from "Who is this user?" to "How is this user connected to this resource?"—the benefits in scalability and maintainability are profound.

Actionable Next Steps:

  • Audit your current logic: Identify where you are performing manual "ownership" checks in your SQL queries or service code.
  • Start Small: Don't migrate your entire auth system at once. Pick a single complex resource (like a Document or a Project) and model it in the OpenFGA Playground.
  • Integrate into CI/CD: OpenFGA models can be versioned and tested. Treat your authorization model as code, with unit tests to ensure that changes don't accidentally grant unintended access.

By centralizing your authorization logic into a relationship-based model, you move away from hard-coded security and toward a flexible, scalable infrastructure that can grow with your product's complexity.