跳至主要内容

Why Multi-Layer Authorization is Necessary

Background

Zitadel provides built-in role management and is often the first place teams implement authorization.

At early stages, using roles such as admin, member, or viewer seems sufficient.
However, as the system grows, relying only on Zitadel roles quickly becomes a limiting factor.

This document explains why Zitadel roles should remain coarse-grained and why fine-grained authorization should be handled elsewhere.


What Zitadel Roles Are Good At

Zitadel roles work well for:

  • High-level access classification
  • Distinguishing admin users from normal users
  • Feature visibility in frontend applications
  • Organization-level permissions

Typical examples:

  • system_admin
  • org_admin
  • org_member

These roles are stable, rarely change, and are closely tied to identity rather than resources.


Where Zitadel Roles Start to Break Down

Lack of Object-Level Permission

Zitadel roles cannot express permissions like:

  • User A can edit document X but not document Y
  • User B is owner of project 123 but only viewer of project 456

Roles are assigned to users, not to relationships between users and resources.

Once you try to model object-level access with roles, you end up with patterns like:

  • project_123_admin
  • project_456_viewer

This does not scale.


Role Explosion Problem

As the number of resources grows, roles multiply rapidly.

For example:

  • 100 projects
  • 3 permission levels per project

That already means hundreds of roles.

Problems that follow:

  • Hard to manage
  • Hard to audit
  • Hard to reason about
  • Hard to remove safely

Roles stop being meaningful and become identifiers for data relationships, which is not their purpose.


No Relationship Awareness

Zitadel roles do not understand relationships such as:

  • Membership inheritance
  • Ownership chains
  • Parent-child resource structures

Examples that are difficult or impossible to model:

  • Organization members automatically gaining access to new projects
  • Team membership granting access to all team resources
  • Shared resources across organizations

These are relationship problems, not identity problems.


No Conditional or Contextual Rules

Zitadel roles are static.

They cannot express conditions such as:

  • Allow access only during business hours
  • Block write access when resource status is archived
  • Restrict actions based on subscription plan
  • Environment-based access differences

Trying to encode conditions into roles leads to:

  • duplicated roles
  • unclear semantics
  • logic leaking into application code

Security and Maintenance Risks

When roles are overloaded to handle authorization logic:

  • Authorization rules leak into backend code
  • Frontend starts making security decisions
  • Small changes require role migrations
  • Auditing becomes difficult

This increases the risk of:

  • accidental privilege escalation
  • inconsistent behavior across services
  • fragile access control logic

A scalable system separates concerns clearly.

Zitadel

  • Authentication
  • User identity
  • Organization and high-level roles
  • Token issuance

Zitadel answers:

Who is the user?


OpenFGA

  • Relationship-based authorization
  • Ownership and membership
  • Object-level permission checks

OpenFGA answers:

Is this user related to this resource in this way?


Cerbos

  • Policy-based access control
  • Conditional rules
  • Business logic enforcement

Cerbos answers:

Given the current context, is this action allowed?


Why This Matters for SaaS and Multi-Tenant Systems

In SaaS systems:

  • Resources grow faster than users
  • Permissions change frequently
  • Business rules evolve over time

Using only roles tightly couples identity with authorization logic, making change expensive and risky.

Separating roles, relationships, and policies allows:

  • independent evolution
  • clearer mental models
  • safer permission changes

Practical Guideline

Use Zitadel roles only when the answer to both questions is yes:

  • Does this permission apply broadly
  • Does it rarely change

If the answer is no, the permission likely belongs in OpenFGA or Cerbos.


Summary

  • Zitadel roles are not wrong, just limited in scope
  • Roles should remain coarse-grained and identity-focused
  • Object-level and conditional authorization require dedicated systems
  • Combining Zitadel, OpenFGA, and Cerbos results in a cleaner and safer architecture

Authorization complexity does not disappear by avoiding it.
It only moves to places where it is harder to see and control.