Yashveer Singh
Connect
<- All posts
SaaS Architecture and Scaling12 min read

The Tenant Aware Permission System: A SaaS Engineer's Guide

A tenant-aware permission system controls what actions a user can take within a specific organization, not just across the product. A user can be an admin in one tenant and a viewer in another. Permissions are scoped to the tenant context, and the enforcement layer always knows which tenant it is operating in. This is the design that B2B SaaS requires and the design that many teams skip in favor of a simpler global role model that does not scale past the first enterprise customer.

Written by Yashveer Singh, founder of Yashveer Labs.

What you actually need to know

  • Permissions in B2B SaaS are always tenant-scoped. A user is an admin in one org and a viewer in another. The global role model breaks on the second enterprise customer.
  • Build the two-layer system from the start: predefined roles for simplicity, per-permission overrides for enterprise edge cases.
  • Every permission check needs the user context and the tenant context. One without the other is incomplete.
  • The permission system is an audit surface. Every change to a role or permission is a logged event.
  • In my experience, the teams that ship a real tenant-aware permission model before their first enterprise deal close significantly faster than the teams that have to retrofit it under sales pressure.
ModelComplexityEnterprise fitCustom permissionsBest for
Global role (admin/user)LowPoorNoSimple consumer SaaS
Tenant-scoped RBACMediumGoodNoMost B2B SaaS
Tenant-scoped RBAC + overridesMedium-highExcellentYesB2B SaaS with enterprise tier
ABACHighExcellentYesComplex multi-resource products
ReBAC (relationship-based)HighExcellentYesProducts with deep resource hierarchies

The core argument

The global role model feels adequate until the moment it is not. The moment is usually the first enterprise prospect who asks, "Can you give our security team read-only access to audit logs but not to customer data?" The global role model has no answer to that question. The permission model that can answer it was not designed into the product. The deal either stalls while the team retrofits the permission system or the team says yes and ships something half-built.

I have watched this happen from both sides. The teams that designed tenant-aware permissions from the start have a confident answer. The teams that did not have an awkward conversation about roadmap timelines in the middle of a sales process.

The data model for a tenant-aware permission system is not complicated. A user can have one or more roles within a tenant. Each role maps to a set of permissions. A permission is a string that identifies an action and optionally a resource type: "projects.create", "reports.view", "billing.manage". The enforcement layer takes the user, the tenant, and the required permission as inputs and returns a boolean.

The enterprise extension adds a permission override table. An override says: for this user in this tenant, grant or deny this specific permission regardless of their role. This is how you handle the security team that needs read access to audit logs but not to the main data. The override is a two-column exception to the role model. It does not require redesigning the whole system.

The failure mode I see most often is not in the initial design. It is in the enforcement. Teams design the right model and then enforce it inconsistently. Some handlers check permissions. Some assume the calling code already checked. The result is a permission system that looks correct in the database and has gaps in the actual enforcement. The fix is centralized enforcement: one middleware or one permission service that every request passes through.

The data model

Core tables

Four tables cover most of the surface. Users, the existing one. Tenants. Memberships, which join users to tenants and carry the role. Roles, with their associated permission sets. The membership row is the thing that determines what a user can do in a given tenant.

The permission set can live in code as a role-to-permissions map, or in the database as a role-permissions join table. In code is simpler and adequate until the product offers custom roles. In the database is necessary once customers can create their own roles, which is a feature most SaaS products eventually need for enterprise.

The override table adds three columns to an existing key: user ID, tenant ID, permission string, granted boolean. Granted false is an explicit deny that overrides the role. Granted true is an explicit allow. The logic is: check the override first; if no override, fall through to the role.

Resource-level grants

Some products need permission at the record level: a user who can edit some projects but not others. This is a resource-level grant. It is a separate table keyed on user, tenant, resource type, resource ID, and permission. The enforcement check asks three questions in order: does the user have the tenant-level permission? If not, do they have a resource-level grant? If not, deny.

How much does this cost

ComponentEngineering timeNotes
Basic tenant-scoped RBACOne to two weeksData model and enforcement middleware
Permission override tableTwo to three daysAddition to existing model
Custom role management UIOne to two weeksFor products that let customers define roles
Resource-level grantsOne weekAdditional table and enforcement layer
Audit trail for permission changesThree to five daysWrite to audit log on every membership change
Permission check performance tuningOne to two daysCache role-permission maps in Redis

What the implementation must get right

  • Enforcement in one place. Middleware or a permission service. Not scattered across handlers.
  • Tenant context always present in the permission check. Never check permission without knowing the tenant.
  • Role-to-permissions map that is consistent and code-reviewed. Bad permission strings become security holes.
  • An explicit deny that overrides any allow. The override table's false value must win.
  • Caching of role-permission maps if the database lookup is in the hot path. But cache invalidation when permissions change.
  • Audit events for every membership change, role change, and override change.
  • A developer-facing permission check function that takes user, tenant, and permission as explicit arguments. No implicit global context.

Expert opinion

The permission model is one of those decisions that looks straightforward and is not. The teams that get it right early have a model that enterprise customers can configure without engineering involvement. The teams that get it wrong early have a support ticket for every permission edge case and a sales conversation they cannot finish without shipping a feature. I have seen the wrong choice cost a team their first enterprise deal.

>

Yashveer Singh, founder of Yashveer Labs

How this played out on a real project

A project management SaaS had been running with a two-role global model for fourteen months: admin or member. The product worked for small teams. When the first mid-market customer arrived, they asked for a billing-only role for their finance team, a read-only role for stakeholders, and the ability to restrict specific projects to specific team members. None of these were possible.

The team spent four weeks building a tenant-scoped RBAC system under sales pressure. The migration from the global role model to the per-tenant model required touching every authorization check in the codebase, and they missed three. Those three gaps became support tickets within a week of launch.

The second version, with centralized enforcement, took another week to finish properly. The total cost of the retrofit was five weeks of engineering on a critical path, plus the delay in closing the deal. For the audit trail that goes alongside this work, audit logs for SaaS a compliance and trust tool covers the logging surface in depth, and the permission system that scales with your b2b customers covers the longer-term scaling of what gets built here.

Common mistakes teams make

  1. Global roles instead of tenant-scoped roles. The model breaks the moment a user is in multiple organizations.
  2. Permission checks scattered across handlers instead of centralized in middleware.
  3. No explicit deny. Absence of a grant is treated as a deny, which is not the same as an explicit deny overriding a role.
  4. Permission strings that are not a controlled vocabulary. Free-text permission names become unsearchable and inconsistent.
  5. No audit trail for permission changes. Support cannot explain why a user lost access.
  6. Caching permission checks without invalidating on change. A removed role stays effective until cache expiry.
  7. No test coverage for permission boundaries. The logic works until an edge case hits production.
  8. Building custom roles before building basic roles. Complexity that arrives before the use case.

A 30 day plan to get this right

  1. Week one. Define the permission vocabulary. What actions exist in the product? Name them with a controlled string format like "resource.action".
  2. Week two. Build the data model. Memberships join table, role-to-permissions map, override table. Write the central permission check function.
  3. Week three. Audit every authorization check in the codebase. Replace scattered checks with calls to the central function.
  4. Week four. Add audit events for every permission change. Test the permission boundaries with integration tests for each role.

For related reading on the broader security picture, the tenant-aware permission system naturally pairs with the security gap how one missing SOC 2 control kills your enterprise deal, which covers how permission gaps surface in enterprise security reviews.

FAQ

Frequently asked

Author

About me and why that should matter to you

Yashveer Singh. Full stack developer. Founder of Yashveer Labs. Based in New Delhi. The reason it should matter to you is that most engineers writing about this topic have not actually done it. I have. The code is on GitHub. The systems are on real URLs. The portfolio has the proof. The contact channel is Instagram. If the work needs to get done, that is how you reach me.

Related reading