Basic RBAC works in early B2B SaaS because the access question is simple:
Can this user perform this action?
That is usually enough when the customer account or tenant is the main boundary. A few roles can carry the product for a long time:
Admin
Manager
User
Viewer
The model starts to strain when the company moves upmarket.
Enterprise customers do not only ask whether a user can perform an action. They ask whether the user can perform that action on specific data, inside a specific scope, under specific conditions, with evidence that explains why access was allowed.
That is where basic RBAC starts turning into authorization debt.
Contents
- You are probably seeing this if
- Early RBAC works because the product model is simple
- Moving upmarket changes the access question
- The common mistake: adding more roles
- Role explosion is a symptom, not the root problem
- What authorization debt looks like
- A simple example
- What to model before adding custom roles
- Closing
- If this is showing up in your product
You are probably seeing this if
- Every enterprise customer creates a new role request.
- Role names now include workspace, project, region, customer, or expiry.
- Custom roles are being added before scope is clearly modeled.
- Support or admin access is handled outside the normal product model.
- Audit logs show what happened, but not why access was allowed.
- Engineers cannot explain effective access without reading scattered application code.
Core argument: RBAC is not the problem. The problem is forcing roles to carry scope, hierarchy, lifecycle, customer exceptions, and audit meaning.
Early RBAC works because the product model is simple
In the early product, the access model is usually close to this:
User -> Role -> Allowed / Denied
This is useful when:
- the tenant is the main boundary
- roles are few
- permissions are coarse
- access is mostly permanent until changed
- customers do not need fine-grained delegation
- audit requirements are basic
At this stage, Admin, Member, and Viewer are often enough.
The team can explain access with a short sentence:
Admins can manage the account. Members can use the product. Viewers can read data.
That simplicity is valuable. You should not make authorization more complex before the customer and product model require it.
Moving upmarket changes the access question
Larger customers bring their own operating model into your product.
They have departments, regions, business units, workspaces, projects, contractors, auditors, customer admins, internal approval rules, support restrictions, and identity lifecycle requirements.
The access question changes from:
Can this user do this?
to:
Who is acting?
What are they trying to do?
Which resource are they trying to access?
Which tenant, workspace, project, or business unit does it belong to?
Is the access temporary, delegated, approved, or conditional?
Can we explain the decision later?
That is no longer just a role check. It is an authorization model.
| Early SaaS access | Enterprise SaaS access |
|---|---|
| Admin / member / viewer | Scoped roles and permissions |
| Tenant is the main boundary | Tenant, workspace, project, resource, region, or business unit |
| Access is mostly permanent | Access may be temporary, delegated, approved, or conditional |
| Logs show activity | Evidence must explain why access was allowed |
| Roles are few | Role requests multiply by customer, scope, and exception |
The common mistake: adding more roles
The usual response is to add another role. Then another. Then another.
Workspace Admin
Project Viewer
Finance Admin
Read-only Billing Admin
External Auditor
Temporary Support Admin
Customer X Export Manager
These role names are understandable. That is why teams reach for them. The problem is that the role name is now doing too many jobs.
It may encode:
- action
- scope
- workspace
- project
- customer segment
- region
- expiry
- approval state
- support context
- audit meaning
This creates role explosion.
10 workspaces x 4 job functions x 3 access variants = 120 roles
The arithmetic looks like the problem, but it is only the visible symptom.
The deeper problem is that every role becomes a security object. It needs ownership, tests, documentation, migration handling, customer explanation, support training, and access review.
That is authorization debt.
Role explosion is a symptom, not the root problem
When roles multiply, the issue is usually not that the team failed to name roles carefully enough.
The issue is that the model is missing separate concepts.
A role is being used to represent things that should be modeled separately:
Role -> grouping of capabilities
Permission -> action that can be performed
Scope -> where the action applies
Resource -> object being accessed
Condition -> runtime rule such as approval, expiry, or support session
Evidence -> explanation of why the decision was allowed
Once enterprise customers need scoped and explainable access, role names are the wrong place to hide those meanings.
You can still use RBAC. You just cannot expect roles alone to carry the whole enterprise access model.
What authorization debt looks like
| Symptom | What it usually means |
|---|---|
| Role names include workspace, project, region, customer, or expiry | Scope is being encoded into role names |
| Every enterprise deal creates another access exception | Sales pressure is turning into architecture debt |
| Custom roles are requested before scope is clear | Customers may configure combinations the system cannot safely enforce |
| Support/admin access bypasses product permissions | Internal access has become a separate access model |
| Audit logs show actions but not decisions | The system cannot explain why access was allowed |
| UI, API, exports, jobs, and webhooks check access differently | Authorization is scattered across surfaces |
These are not cosmetic problems. They are signals that the product’s access model no longer matches the customer’s operating model.
A simple example
Assume an enterprise customer asks for this:
Finance users should export invoices, but they should not see engineering projects.
A basic RBAC implementation may respond by adding:
Finance Export Manager
That may work for the first customer. Then another customer asks:
Regional admins should manage only users in EMEA.
Now you add:
EMEA User Admin
Then another asks:
Contractors should access one project for 30 days.
Now you add:
Temporary Project Contributor
The pattern looks harmless because each request is reasonable. The debt appears later, when the team has to answer:
- Which data can this user access?
- Why was access allowed?
- Who granted it?
- Where does the access apply?
- When does it expire?
- Does the API enforce the same rule as the UI?
- Can the customer audit it?
If those answers require reading scattered code paths and interpreting role names, the model has already become fragile.
What to model before adding custom roles
Before adding custom roles, define the authorization primitives.
- Permission catalog: the actions the system understands.
- Resource model: the objects those actions apply to.
- Scope model: tenant, workspace, project, region, business unit, or resource hierarchy.
- Grant model: who receives which role or permission, over which scope.
- Condition model: expiry, approval, support session, break-glass, or delegation.
- Decision path: one enforcement path used by UI, API, exports, jobs, and webhooks.
- Evidence model: what gets recorded when access is allowed or denied.
Custom roles are useful only after these primitives exist. Otherwise, customers are configuring names, not enforceable access rules.
For example, avoid turning this request into another role name:
Finance users in EMEA can export invoices for 90 days.
A fragile model creates a role like this:
Temporary EMEA Finance Export Manager
That role name now carries job function, region, permission, and expiry. It may work once, but it does not scale.
A healthier model separates the concepts:
Role:
Invoice Exporter
Permissions:
invoice.read
invoice.export
Assignment:
principal: finance_group
role: Invoice Exporter
visibility_boundary: EMEA finance records
conditions:
expires_at: 2026-09-30
approval_id: approval_123
Now the system can answer the important questions:
- Who received access?
- What can they do?
- Where does the access apply?
- Who approved it?
- When does it expire?
- Why was access allowed?
The role remains simple. The scope, lifecycle, approval, and audit evidence are modeled explicitly instead of being hidden inside the role name.
Closing
Basic RBAC works when the product has a simple access model and the tenant is the main boundary.
It starts to break when enterprise customers need scoped access, delegated administration, temporary access, customer-specific controls, and audit evidence.
The practical question is not whether RBAC is obsolete.
The question is whether your product is forcing roles to carry responsibilities that belong to scope, resource, condition, policy, and evidence layers.
When that happens, more roles are not the solution.
They are the symptom.
If this is showing up in your product
I review authorization, tenant-isolation, and internal access models for B2B SaaS companies preparing for enterprise customers.
The review is useful when your team is adding custom roles, delegated administration, support access, SSO/SCIM, API keys, service accounts, or audit evidence, and wants to find unclear ownership, inconsistent enforcement paths, tenant boundary risks, and weak access explanations before they show up in enterprise security reviews.