π Multi-Tenant Data Isolation
SaaS tenant separation architecture and enforcement mechanisms
Executive Summary
AIUsagePlatform implements shared-database multi-tenancy with row-level security. Each tenant's data is isolated via OrganizationId foreign keys across all tenant-scoped tables. The MultiTenantIsolationMiddleware enforces this isolation at the request level.
β
Row-Level Security
OrganizationId FK on all tenant tables
OrganizationId FK on all tenant tables
β
Middleware Enforcement
Automatic tenant context extraction
Automatic tenant context extraction
β
SuperAdmin Bypass
Controlled cross-tenant access for admins
Controlled cross-tenant access for admins
β οΈ No DB Row Security
Relies on app layer, not DB RLS
Relies on app layer, not DB RLS
ποΈ Multi-Tenant Architecture
Shared Database Multi-Tenancy Model
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β SINGLE DATABASE - MULTIPLE TENANTS β
β (Shared Schema Approach) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Organizations Table β β
β β ββββββββββββββββ¬βββββββββββββββββ¬βββββββββββ¬βββββββββββ β β
β β β Id β Name β Plan β Status β β β
β β ββββββββββββββββΌβββββββββββββββββΌβββββββββββΌβββββββββββ€ β β
β β β org-acme β Acme Corp β Pro β Active β β β
β β β org-tech β TechStart Inc β Basic β Active β β β
β β β org-global β GlobalCo βEnteprise βSuspended β β β
β β ββββββββββββββββ΄βββββββββββββββββ΄βββββββββββ΄βββββββββββ β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Users Table (Tenant-Scoped) β β
β β ββββββββββββββββ¬βββββββββββββββ¬βββββββββββββββ¬βββββββββββ β β
β β β Id β Email β OrganizationIdβ Role β β β
β β ββββββββββββββββΌβββββββββββββββΌβββββββββββββββΌβββββββββββ€ β β
β β β usr-john βjohn@acme.com β org-acme β Admin β β β
β β β usr-jane βjane@acme.com β org-acme β User β β β
β β β usr-bob β bob@tech.com β org-tech β Admin β β β
β β ββββββββββββββββ΄βββββββββββββββ΄βββββββββββββββ΄βββββββββββ β β
β β β β
β β β CRITICAL: Every tenant-scoped table has OrganizationId FK β β
β β β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Sessions Table (Tenant-Scoped) β β
β β ββββββββββββββββ¬βββββββββββββββ¬βββββββββββββββ¬βββββββββββββ β β
β β β Id β UserId β OrganizationIdβ Start β β β
β β ββββββββββββββββΌβββββββββββββββΌβββββββββββββββΌβββββββββββββ€ β β
β β β ses-001 β usr-john β org-acme β 2026-01-15 β β β
β β β ses-002 β usr-jane β org-acme β 2026-01-15 β β β
β β β ses-003 β usr-bob β org-tech β 2026-01-15 β β β
β β ββββββββββββββββ΄βββββββββββββββ΄βββββββββββββββ΄βββββββββββββ β β
β β β β
β β Query Pattern: β β
β β SELECT * FROM Sessions WHERE OrganizationId = @orgId β β
β β β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β Tenant Isolation Benefits: β
β β’ β
Single codebase for all tenants β
β β’ β
Simplified backups and migrations β
β β’ β
Cross-tenant analytics possible (SuperAdmin) β
β β’ β οΈ Risk of data leakage if queries miss WHERE OrganizationId clause β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
π MultiTenantIsolationMiddleware
Middleware Execution Flow
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Incoming Request
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β MultiTenantIsolationMiddleware (Custom) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β Step 1: Bypass Check β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β IF path is in bypass list (auth, swagger, health, agent-onboarding) β
β β Skip tenant validation, continue to next middleware β
β β
β Step 2: Extract Tenant Context β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β Primary: Extract OrganizationId from JWT claims (sub claim) β
β Fallback: Check X-Organization-Key header (for service accounts) β
β β
β IF no OrganizationId found β
β β Return 401 Unauthorized β
β β
β Step 3: SuperAdmin Check β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β IF user has SuperAdmin role β
β β BYPASS all tenant checks (explicit permission) β
β β Set IsSuperAdmin = true in TenantContext β
β β Continue (SuperAdmin can access all tenants) β
β β
β Step 4: Validate Organization β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β Query Organization table: β
β SELECT * FROM Organizations WHERE Id = @orgId β
β β
β IF organization not found β
β β Return 403 Forbidden "Invalid organization" β
β β
β IF organization.Status != Active β
β β Return 403 Forbidden "Organization suspended/expired" β
β β
β Step 5: Subscription Validation β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β Check Subscription: β
β SELECT * FROM Subscriptions β
β WHERE OrganizationId = @orgId AND Status = 'Active' β
β AND EndDate > GETUTCDATE() β
β β
β IF no active subscription β
β β Return 402 Payment Required "Subscription expired" β
β β
β Step 6: Feature Gate Check (Optional) β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β IF endpoint has [RequiresPlanFeature] attribute β
β β Check if feature is enabled in organization's plan β
β β Return 403 if feature not available β
β β
β Step 7: Rate Limiting Check β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β Check Redis for rate limit counters per OrganizationId β
β β Return 429 Too Many Requests if exceeded β
β β
β Step 8: Attach Tenant Context β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β Store in HttpContext.Items: β
β β’ "OrganizationId" β extracted org ID β
β β’ "IsSuperAdmin" β true/false β
β β
β Attach Response Headers: β
β β’ X-Tenant-Id: org_xxx β
β β’ X-Tenant-Status: Active β
β β
β Step 9: Continue Pipeline β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Call next middleware/controller β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
π‘οΈ Isolation Enforcement Points
| Layer | Enforcement Mechanism | Responsibility |
|---|---|---|
| Middleware | MultiTenantIsolationMiddleware | Extract tenant, validate status, attach context |
| Controller | BaseApiController.GetCurrentOrganizationId() | Access tenant context from claims |
| Service | OrganizationId parameter on all methods | Pass tenant context to repositories |
| Repository | WHERE OrganizationId = @orgId on all queries | Enforce row-level isolation |
| Database | Foreign key constraints | Referential integrity, cascade delete |
π Tenant Data Access Patterns
Standard User Flow
User Login
β
JWT Created: { sub: usr-123, org: org-456 }
β
Request /api/sessions
β
Middleware: Extract org-456 from JWT
β
Validate org-456 exists & Active
β
Repository Query:
SELECT * FROM Sessions
WHERE OrganizationId = 'org-456'
AND UserId = 'usr-123'
β
Return: Only user's org sessions
SuperAdmin Flow
SuperAdmin Login
β
JWT Created: { sub: usr-admin, role: SuperAdmin }
β
Request /api/adminanalytics
β
Middleware: Detect SuperAdmin role
β
BYPASS tenant validation
β
Set IsSuperAdmin = true
β
Service Query:
SELECT * FROM Sessions
(no WHERE OrganizationId!)
β
Return: All tenants' data
β οΈ Security Risks & Mitigations
Missing WHERE Clause
Developers may forget OrganizationId filter. Mitigation: Code review, integration tests, repository base class enforcement.
SuperAdmin Privilege Escalation
Compromised SuperAdmin account = full platform access. Mitigation: IP whitelisting, MFA, audit logging.
Defense in Depth
Multiple enforcement layers (middleware, service, repository) reduce single-point-of-failure risk.
Status Validation
Suspended/expired organizations automatically blocked at middleware layer before reaching business logic.
β Recommended Enhancements
Repository Base Class
Create TenantRepositoryBase
Recommended
Integration Tests
Automated tests that verify Tenant A cannot access Tenant B's data for every endpoint.
Recommended
SQL Server RLS
Enable Row-Level Security policies in SQL Server as additional defense layer.
Recommended