RBAC Design in SaaS Applications
RBAC in SaaS is not just roles and permissions. Learn how authorization design can prevent BOLA, cross-tenant access, and broken permission boundaries.
On this page
- Authorization Boundary in Multi Tenant SaaS
- Roles vs Permissions
- Tenant Scoped Authorization
- Modeling RBAC in a Relational Database
- Authorization in ASP.NET Core
- Preventing Privilege Escalation
- Broken Access Control Failure Scenario
- Authorization and Data Isolation
- Policy Composition
- Operational Considerations
- Engineering Checklist
- Closing Thoughts
RBAC Design in SaaS Applications
Most SaaS authorization failures do not originate from missing authentication. They originate from poorly designed authorization boundaries.
Systems authenticate users successfully and then proceed to grant excessive authority across tenants, modules, or data domains. The result is broken access control. In multi tenant SaaS systems this class of failure often leads to cross organization data exposure or privilege escalation.
Role based access control is commonly presented as a simple concept. In practice it becomes one of the most structurally important layers of SaaS architecture. It defines how authority propagates through the application, how actions are constrained by tenant context, and how sensitive operations are protected.
This article focuses on implementation level RBAC design for multi tenant SaaS systems built with ASP.NET Core and relational databases.
If you’re building a SaaS product, this is exactly the kind of issue that appears when tenant boundaries are not designed properly. Teams that need SaaS development for secure multi-tenant systems usually define roles, permissions, and data ownership together.
If you need to validate the live request path, the SaaS RBAC Audit and Broken Access Control Audit test the same role boundaries against real API behavior.
Want your SaaS authorization tested against real tenant boundaries?
RBAC can look correct while object-level authorization still fails. We test role rules, tenant context, BOLA paths, and response differences that expose another customer’s data.
For system-level context, pair this with SaaS Security Architecture: A Practical Engineering Guide and Organization-Level Data Isolation in Multi-Tenant SaaS.
Authorization Boundary in Multi Tenant SaaS
In a multi tenant system the authorization boundary is not simply user identity. It is the combination of identity and tenant context.
A request entering the system carries several pieces of information:
- authenticated user identity
- tenant identifier
- role assignments within that tenant
- action being attempted
Authorization decisions must incorporate all four.
This is not just an implementation detail. This is a system design problem.
If you’re building a SaaS product, this is the level where architecture decisions start affecting security, cost, and product behavior. SaaS development services are relevant when authorization has to remain tenant-scoped across the request pipeline.
Typical request pipeline:
HTTP Request
->
Authentication
->
Tenant Resolution Middleware
->
Tenant Context Injection
->
Authorization Policy Evaluation
->
Application Logic
Tenant resolution and authorization are tightly coupled.
Roles vs Permissions
RBAC systems combine two concepts.
Roles represent organizational responsibility.
Examples:
- Owner
- Administrator
- Manager
- Analyst
- Viewer
Permissions represent specific capabilities.
Examples:
- users.read
- users.create
- billing.update
- auditlog.export
- tenant.settings.update
Roles map to collections of permissions.
Roles should rarely be hardcoded in application logic.
Example of problematic logic:
if(user.Role == "Admin")
{
// allow operation
}Permissions provide a more stable abstraction for authorization policies.
Tenant Scoped Authorization
A defining property of SaaS RBAC systems is tenant scope. This is also the core boundary tested in a tenant isolation audit.
A user may hold different roles in different organizations.
If roles look correct but tenant scope is still uncertain, a tenant isolation and RBAC audit can test whether permissions and tenant boundaries hold together.
Example:
User: alice@example.com
Tenant A -> Administrator
Tenant B -> Viewer
The database must model role membership as:
User + Tenant + Role
Example schema:
Users Id
Email
PasswordHash
Tenants Id
Name
Roles Id
Name
Permissions Id
Key
RolePermissions RoleId
PermissionId
UserTenantRoles UserId
TenantId
RoleId
This model prevents global role assignments across tenants.
Modeling RBAC in a Relational Database
Authorization checks occur frequently so relational modeling must support efficient queries.
Example query used to evaluate permissions:
SELECT p.Key
FROM Permissions p
JOIN RolePermissions rp ON rp.PermissionId = p.Id
JOIN UserTenantRoles utr ON utr.RoleId = rp.RoleId
WHERE utr.UserId = @UserId
AND utr.TenantId = @TenantIdRecommended indexes:
- UserTenantRoles(UserId, TenantId)
- RolePermissions(RoleId)
- Permissions(Key)
High scale systems often cache effective permissions in memory.
Cache keys must include tenant identifiers.
Authorization in ASP.NET Core
ASP.NET Core provides a policy based authorization framework.
Example policy definition:
services.AddAuthorization(options =>
{
options.AddPolicy("UsersRead", policy =>
policy.Requirements.Add(new PermissionRequirement("users.read")));
options.AddPolicy("UsersCreate", policy =>
policy.Requirements.Add(new PermissionRequirement("users.create")));
});Permission requirement:
public class PermissionRequirement : IAuthorizationRequirement
{
public string Permission { get; }
public PermissionRequirement(string permission)
{
Permission = permission;
}
}Authorization handler:
public class PermissionHandler : AuthorizationHandler<PermissionRequirement>
{
private readonly IPermissionService _permissions;
public PermissionHandler(IPermissionService permissions)
{
_permissions = permissions;
}
protected override async Task HandleRequirementAsync(
AuthorizationHandlerContext context,
PermissionRequirement requirement)
{
var userId = context.User.GetUserId();
var tenantId = context.User.GetTenantId();
if (await _permissions.UserHasPermission(userId, tenantId, requirement.Permission))
{
context.Succeed(requirement);
}
}
}Controllers enforce policies.
[Authorize(Policy = "UsersCreate")]
[HttpPost]
public async Task<IActionResult> CreateUser(CreateUserRequest request)
{
...
}Preventing Privilege Escalation
Privilege escalation often occurs through administrative endpoints.
Common vectors:
- role editing APIs
- tenant settings updates
- user invitation flows
- role assignment endpoints
Example vulnerability:
POST /api/users/{id}/role
If administrators can assign any role including Owner they can escalate privileges.
Safer implementation validates role hierarchy.
Owner
Administrator
Manager
Viewer
Users may only assign roles lower than their own.
Example validation:
if(targetRole.Level >= currentUserRole.Level)
{
return Forbid();
}Broken Access Control Failure Scenario
Example endpoint:
var project = await _db.Projects
.FirstOrDefaultAsync(p => p.Id == projectId);The query ignores tenant boundaries.
If project identifiers are guessable a user from Tenant A may retrieve projects belonging to Tenant B.
Correct query:
var project = await _db.Projects
.Where(p => p.TenantId == tenantId)
.FirstOrDefaultAsync(p => p.Id == projectId);Even better is enforcing isolation using EF Core global query filters.
modelBuilder.Entity<Project>()
.HasQueryFilter(p => p.TenantId == _tenantContext.TenantId);RBAC alone cannot prevent this class of failure, which is why teams pair permission design with an external review before incidents.
The remaining risk is usually drift between role policy, tenant scope, and the query that actually executes.
This is where API authorization testing for SaaS is useful: it checks whether role rules still hold when the same object is replayed under different actors and tenant contexts. Tenant isolation testing for SaaS covers the same boundary from the tenant-scoping side, and the authorization testing checklist for multi-tenant APIs gives teams a more structured way to validate the same paths. Teams that need a manual review of policy enforcement and live request paths usually start with an API authorization audit for SaaS systems.
Authorization and Data Isolation
RBAC and tenant isolation must reinforce each other.
Tenant isolation protects data boundaries.
RBAC protects action boundaries.
Typical evaluation sequence:
Authentication
Tenant Resolution
Tenant Data Isolation
Authorization Policy
Application Logic
Authorization policies must never execute without tenant context.
Policy Composition
Complex SaaS systems require composed authorization policies.
Example:
options.AddPolicy("EditDocument", policy =>
{
policy.Requirements.Add(new PermissionRequirement("documents.edit"));
policy.Requirements.Add(new DocumentOwnerRequirement());
});Each requirement is evaluated independently.
This keeps controllers free from complex authorization logic.
Operational Considerations
RBAC systems must remain observable and auditable.
Recommended log fields:
- UserId
- TenantId
- Permission evaluated
- Resource identifier
- Request path
Audit logs should record:
- role assignment
- permission changes
- tenant ownership transfer
Without these logs security incidents are difficult to investigate.
Engineering Checklist
A minimal RBAC implementation should ensure:
- roles are scoped to tenants
- permissions represent system capabilities
- authorization policies rely on permissions
- role assignment endpoints enforce hierarchy
- database queries enforce tenant isolation
- authorization decisions are logged
- permission caching includes tenant context
Closing Thoughts
RBAC is structural infrastructure inside a SaaS system.
Weak RBAC designs eventually manifest as broken access control and cross organization data exposure.
Strong implementations combine:
- tenant scoped identity
- permission driven authorization
- strict tenant data isolation
ASP.NET Core provides the primitives to build this model but the architecture must be designed intentionally.
If you’re building or planning a SaaS product, SaaS development for secure multi-tenant systems is most useful when RBAC still needs to be shaped around the product model.
Related Articles
Need a SaaS security review?
Check where authorization, tenant boundaries, and audit trails can fail before they turn into an incident.
SaaS Security Cluster
This article is part of our SaaS security architecture and audit series.
SaaS Security Architecture: A Practical Engineering Guide