Multi-Tenant Authorization
In B2B SaaS, the worst possible bug is one tenant seeing another tenant's data. This guide covers the patterns, pitfalls, and best practices for multi-tenant authorization at scale.
On this page
The isolation problem
Every authorization decision must be answered within a tenant boundary. "Can this user delete invoice 123?" is meaningless without "in which tenant?" The tenant must be an explicit, non-optional input to every check — never inferred from the resource alone. Inferring the scope from a resource ID or JWT claim is exactly how cross-tenant data leaks happen.
Common vulnerability
If your access check resolves the tenant ID from the invoice ID (invoiceIdToTenant(invoiceId)), a crafted request with a tenant B invoice ID in a tenant A session leaks data across tenants. Always require the tenant ID as an explicit parameter gated by the user's session, not the resource.
Pattern 1: Scope-per-tenant
Model each tenant as a scope — a hard boundary that namespaces all roles, permissions, and policies. Every permission, role, and access policy lives under exactly one scope. Subjects' access in one scope is completely independent from another.
// At tenant creation:
await createScope({ id: 'acme-corp', name: 'Acme Corp' })
await seedDefaultRoles('acme-corp') // Admin, Member, Viewer
// On every access check:
await hasAccess({
subjectId: user.id,
scopeId: 'acme-corp', // explicit, never optional
resource: 'invoice',
action: 'delete',
})Pattern 2: Shared vs per-tenant roles
Most SaaS products want common roles (Admin/Member/Viewer) across all tenants, but let enterprise customers define custom roles. Support both by seeding a default role set into each new scope, while allowing per-scope additions. Enterprise customers add roles inside their own scope — invisible to other tenants.
Pattern 3: Cross-tenant support access
Your support team occasionally needs into a customer's workspace. Do not give them a god-mode API key. Instead, grant time-boxed, audited, per-scope access — and log every action with the support agent's identity, the target scope, and the reason. Cross-tenant access should be the rare, loud exception.
Pattern 4: Tenant-aware API keys
Issue scoped API keys so your customers' backend services can call your API on behalf of their workspace. Keys are bound to a scope and a permission set — a key for Tenant A cannot access Tenant B's data. Rotate and audit independently.
Isolation pitfalls
| Pitfall | Consequence | Mitigation |
|---|---|---|
| Scope inferred from resource | Cross-tenant data leak | Require scope as explicit parameter |
| Global admin roles | One compromised key exposes all tenants | Scope every role; time-box support access |
| Shared role IDs across tenants | Edits bleed between customers | Namespace roles under the scope |
| No per-tenant audit | Cannot answer "who accessed my data" | Log every decision with scope + subject |
Pricing: flat-rate vs per-tenant
Many authorization platforms charge per tenant (Permit.io caps free tier at 20 tenants) or per connection (WorkOS charges $125/connection/month for SSO). WardenAuth has unlimited tenants on every tier — including Free. Your bill is based on the number of access checks, not the number of customers.
Implementation guides
Start building multi-tenant today
Unlimited scopes on every tier. No per-tenant charges. Start free — no credit card.
Get started