Broken Access Control in SaaS Platforms

How broken access control emerges in SaaS architectures and how to enforce authorization boundaries across tenants, roles, and resources.

Broken Access Control in SaaS Platforms

Use this with Secure API Authentication vs Authorization, What Is BOLA and Why It Breaks SaaS APIs, and RBAC Design in SaaS Applications for a full authorization hardening model.

For practical broken access control testing across APIs, use Agnite Scan.

When those checks are spread across controllers, services, and data access code, a SaaS security audit helps verify that the enforcement still holds under real object and tenant permutations.

If you’re building a SaaS product, this is the point where authorization stops being a local code check and becomes a system boundary. Teams that need to build a system like this usually design access control with the rest of the platform.

Broken access control is the most common critical vulnerability in modern SaaS systems. It appears repeatedly in public breach disclosures and security reports because authorization is not a single mechanism. It is an architectural property of the entire system.

Authentication determines who the user is. Access control determines what the user is allowed to do.

In SaaS platforms authorization must operate across several dimensions:

  • tenant isolation
  • user identity
  • organization roles
  • resource ownership
  • service boundaries

If authorization logic is not designed as a system-wide architectural layer, small implementation mistakes accumulate until privilege boundaries collapse.

This article examines how broken access control emerges inside SaaS architectures and how engineering design must prevent it.


Problem Definition and System Boundary

A SaaS platform typically exposes a public API surface structured like this:

Browser

API Gateway / Load Balancer

Application Services

Database

Within that request path several security assumptions must hold simultaneously.

A request must prove:

  • the identity of the user
  • the organization or tenant the user belongs to
  • the roles and permissions granted to that user
  • ownership of the resource being accessed

If any of these checks is missing or implemented inconsistently, the system becomes vulnerable to broken access control.

Authorization logic is often scattered across:

  • API controllers
  • service layer logic
  • database queries
  • background jobs
  • internal service APIs

Fragmentation increases the probability of inconsistent enforcement.

Example:

GET /api/projects/8472

The endpoint may verify authentication but fail to verify whether the project belongs to the user’s tenant.

That missing constraint becomes a cross-tenant data exposure.


Why Broken Access Control Appears in SaaS Systems

Access control failures rarely originate from a single bug. They usually emerge from architectural shortcuts.

Implicit Authorization Assumptions

Developers assume upstream layers already performed checks.

Example assumption:

Controller verified tenant access.

A service method later performs an unrestricted query.

var project = await db.Projects.FindAsync(projectId);

If another endpoint calls the same service, the authorization guarantee disappears.

Authorization must never depend on fragile assumptions between layers.


Resource Identifier Exposure

APIs frequently expose numeric or GUID identifiers.

Example:

GET /api/invoices/51273

If the system checks authentication but not ownership, attackers can enumerate identifiers and access resources belonging to other tenants.

This vulnerability class is often called IDOR.

In SaaS environments this becomes a tenant isolation failure.


Authorization Outside the Data Layer

Another common mistake is performing authorization checks after loading the resource.

Example:

if (invoice.OrganizationId != user.OrganizationId)
{
    throw new UnauthorizedException();
}

return invoice;

Problems with this pattern:

  • unauthorized data is loaded first
  • checks may be forgotten
  • duplication occurs across endpoints

Authorization constraints should be embedded in queries whenever possible.


Architectural Patterns for Strong Access Control

Reliable access control emerges when authorization constraints are embedded directly into architecture.

Tenant-Scoped Data Access

Tenant isolation forms the first security boundary.

Example EF Core configuration:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Project>()
        .HasQueryFilter(p => p.OrganizationId == _tenantContext.OrganizationId);
}

Global query filters automatically enforce tenant isolation.

Query flow:

Request → TenantContext → EF Core Query Filter → Database

Developers no longer need to remember tenant checks manually.


Resource Ownership Validation

Tenant isolation alone is insufficient.

Users inside the same tenant may have different permissions.

Example roles:

  • organization admin
  • team member
  • billing manager
  • viewer

ASP.NET Core policy example:

services.AddAuthorization(options =>
{
    options.AddPolicy("ProjectWrite", policy =>
        policy.RequireClaim("role", "admin", "editor"));
});

Endpoint:

[Authorize(Policy = "ProjectWrite")]
[HttpPost("/projects/{id}")]
public async Task<IActionResult> UpdateProject(Guid id)
{
    ...
}

Policies centralize authorization logic.


Ownership-Based Access Checks

Some resources require additional ownership validation.

Example query:

var project = await db.Projects
    .Where(p => p.Id == projectId &&
                p.OrganizationId == tenantId &&
                p.OwnerUserId == currentUserId)
    .FirstOrDefaultAsync();

The authorization constraint becomes part of the query.

Unauthorized resources cannot be loaded.


Service-Level Authorization Guards

Large SaaS systems introduce internal service layers.

Authorization must exist there as well.

Example:

public async Task<Project> GetProjectForUser(Guid projectId, UserContext user)
{
    return await db.Projects
        .Where(p => p.Id == projectId &&
                    p.OrganizationId == user.OrganizationId)
        .FirstOrDefaultAsync();
}

Controllers call authorization-aware services rather than raw repositories.


Implementation Example in ASP.NET Core

A typical SaaS authorization architecture includes multiple layers.

Identity Layer

Responsible for authentication and token issuance.

Example token payload:

{
  "sub": "user_24817",
  "org": "org_912",
  "roles": ["admin"]
}

Tenant Context Middleware

Extracts tenant identity from the request.

public class TenantContextMiddleware
{
    private readonly RequestDelegate _next;

    public async Task Invoke(HttpContext context, TenantContext tenant)
    {
        tenant.OrganizationId = context.User.FindFirst("org")?.Value;
        await _next(context);
    }
}

Authorization Policies

Example policy:

options.AddPolicy("BillingAccess", policy =>
{
    policy.RequireAssertion(ctx =>
        ctx.User.HasClaim("role", "admin") ||
        ctx.User.HasClaim("role", "billing"));
});

Data Layer Enforcement

Tenant filtering must exist in database queries.

var invoices = await db.Invoices
    .Where(i => i.OrganizationId == tenantId)
    .ToListAsync();

Even if API logic fails, the data layer still enforces isolation.

This layered design reduces catastrophic mistakes.


Real Failure Scenario

Consider a SaaS billing platform.

Invoices table:

Invoices Id OrganizationId Amount Status CreatedAt

Endpoint:

GET /api/invoices/{id}

Initial implementation:

[Authorize]
public async Task<IActionResult> GetInvoice(Guid id)
{
    var invoice = await db.Invoices.FindAsync(id);
    return Ok(invoice);
}

The endpoint checks authentication but not tenant ownership.

Attack scenario:

  1. attacker logs in
  2. attacker requests /api/invoices/1001
  3. attacker increments identifiers
  4. attacker retrieves invoices from other tenants

The fix embeds tenant filtering in the query.

var invoice = await db.Invoices
    .Where(i => i.Id == id &&
                i.OrganizationId == tenantId)
    .FirstOrDefaultAsync();

Even if arbitrary IDs are provided, unauthorized data cannot be returned.


Operational Considerations

Security Testing

Recommended tests:

  • ID enumeration testing
  • cross-tenant API fuzzing
  • privilege escalation attempts
  • automated authorization tests

Example test:

Tenant A user attempts to access Tenant B resource → expect 403 or 404.


Observability and Audit Logging

Access violations should be observable.

Example audit event:

EventType: AuthorizationFailure UserId: user_24817 Resource: Invoice ResourceId: 51273 OrganizationId: org_912 Timestamp: …

Security monitoring systems can detect enumeration attempts or privilege escalation patterns.


Authorization Code Reviews

Code review checklist:

  • resource queries include tenant constraints
  • ownership rules enforced
  • authorization policies defined
  • service layer does not bypass checks

Broken access control often enters through small overlooked shortcuts.

These gaps usually stay hidden until the same permissions are exercised across multiple tenant and object combinations.


Validate broken access control before release

We review tenant boundaries, ownership checks, and authorization paths across the API and data layer. The goal is to catch object-level access failures while they are still reproducible in testing.

Linking Back to the Pillar

Broken access control is not a single bug class. It represents a failure of architectural boundaries.

Secure SaaS systems treat authorization as a system-wide constraint embedded in:

When these layers cooperate, access control remains resilient even if individual endpoints contain mistakes.

If authorization now spans controllers, services, caches, and data access, this is the point where a SaaS security audit is usually needed to verify the boundary under real request conditions.

If you need to validate the full chain in a live system, a broken access control audit checks ownership rules, tenant boundaries, and data-layer enforcement together.

For a broader architectural perspective, see SaaS Security Architecture: A Practical Engineering Guide.

Need implementation support? Review the API security testing tool or explore our services.

Continue reading in SaaS Security

Building SaaS with complex authorization?

Move from theory to request-level validation and architecture decisions that hold under scale.

SaaS Security Cluster

This article is part of our SaaS Security Architecture series.

Start with the pillar article: SaaS Security Architecture: A Practical Engineering Guide