Skip to content
Back to blog

Engineering

Reading time
7 min read
Published
Updated

Editorial note

Test every tenant boundary before release

BarmajTek EngineeringEngineeringReviewed on August 8, 2026
Test every tenant boundary before release

This article covers “Test every tenant boundary before release” under the topic “Multi-tenant authorization testing checklist,” written as operating guidance a team can apply directly.

Multi-tenant authorization testing asks a stricter question than “does every query include a tenant filter?” A SaaS request crosses routes, policies, database connections, queues, caches, files, exports, notifications, administration, and support tools. A single missing boundary can expose another customer’s record even when the main model uses a global scope.

Build the test plan around assets and attacker actions. Name the tenant identifier, the user roles, sensitive records, privileged workflows, and background processes. Then prove both allowed and denied behavior with two real tenant fixtures. A test that only confirms Tenant A can read Tenant A does not prove Tenant A cannot read Tenant B.

Start with a boundary inventory

List every place that accepts or reconstructs tenant context: host, route parameter, authenticated membership, request body, signed link, job payload, scheduled loop, cache key, storage path, export, webhook reference, and support session. Mark the authoritative source for each path and reject conflicting input rather than quietly choosing one.

Include central platform operations separately. A super-admin connection or reporting role may need broader access, but that capability must not leak into normal requests or queue workers. Record which process may use it and what audit evidence it produces.

Test route and request authorization

Create two tenants, two owners, and representative lower-privilege roles. For every show, update, delete, download, and action route, attempt the operation with an identifier from the other tenant. Expect a controlled denial or not-found response, never a server error and never a partial payload.

Do not stop at controller model binding. Test nested identifiers, bulk actions, search endpoints, autocomplete, validation rules, and indirect objects such as invoice line, attachment, or comment. Authorization should derive ownership from the protected record, not trust a tenant ID supplied beside it.

Enforce the database boundary

Application policies are necessary but vulnerable to forgotten code paths. PostgreSQL row-level security can provide a second boundary when the runtime role is non-owner and cannot bypass RLS. Verify the actual production-style role, policies, current tenant setting, and owner-only migration connection.

Run cross-tenant selects, updates, inserts, and deletes through the runtime connection. Test missing context and invalid context fail closed. Also test raw queries and relationships that bypass model conveniences. A policy that works only in local owner-role tests is not evidence of production isolation.

Test queued jobs and scheduled work

A request may dispatch a job after tenant middleware has ended. Put an immutable tenant reference in the payload, restore context before loading tenant records, and clear it afterward. Test that a job for Tenant A cannot accept a record ID from Tenant B, even when both IDs exist.

Exercise retries, failed-job replay, chained jobs, notifications, and batches. Workers are long-running, so add a test that runs work for A and then B in the same process and proves no context survives. For scheduled commands, iterate explicit tenants and isolate each transaction and failure.

Partition caches, locks, and rate limits

Every cache entry containing tenant data needs a tenant-qualified key. Test same record IDs or human-readable slugs in two tenants and confirm the values do not collide. Include computed dashboard data, feature state, permissions, search results, locks, and idempotency keys.

Do the same for rate limits. A noisy customer should not consume another customer’s allowance unless a documented global limit intentionally applies. Verify invalidation removes only the owning tenant’s keys and never relies on a broad cache flush in normal operation.

Protect files and generated exports

Storage paths need tenant ownership that the application verifies before issuing a download. Random filenames or private disks are not authorization. Test a copied URL, guessed object key, stale signed link, and a database row whose file path was replaced with another tenant’s path.

Exports are especially risky because they join many tables outside ordinary screens. Maintain an inventory of included domains and test every exported row carries the expected tenant ownership. Store generated packages privately, expire them, audit downloads, and remove partial files after failure or cancellation.

Test events, webhooks, and notifications

Incoming provider references must resolve to the tenant-owned integration or transaction before any update. Do not accept a tenant ID from the payload as proof. Test replay, unknown references, wrong provider, and a validly signed event that points to another tenant’s internal record.

For outbound events, assert channel authorization and payload minimization. A private channel name is not enough if its authorization callback ignores tenant membership. Verify mail, realtime events, analytics, and logs do not include another tenant’s names, contacts, or identifiers.

Constrain support and impersonation

Support tools should require a reason, actor, target tenant, expiry, and audit record. Test that normal platform users cannot acquire the privileged connection and that support access does not silently become a tenant owner session. Limit sensitive actions even during approved access.

Impersonation also needs a clear exit and session rotation. Verify links, exports, and queued actions created during support remain attributed to the real operator. A screenshot of a support banner is not an authorization control.

Build a negative regression matrix

For each tenant table or aggregate, record read, create, update, delete, action, export, and background coverage. Include an allowed case and a cross-tenant denial. Generate the matrix from the schema or a maintained catalog so a new table cannot arrive without tests.

Add property-based or factory variations where identifiers, slugs, and roles overlap. Keep failure assertions precise: 403, 404, empty result, or database policy error according to the contract. A 500 may hide a denial bug and should fail the release.

Run production-shaped acceptance

Static analysis and unit policies cannot prove role configuration. In CI, use PostgreSQL with RLS, the runtime role, Redis-like cache semantics where relevant, private storage, and real queue serialization. Test configuration caching and long-running worker reuse.

Before release, review migrations for owner role, policy activation, grants, and default privileges. Run a small smoke matrix after deployment without accessing real tenant data. Monitor denied operations and unusual privileged connection use without logging sensitive payloads.

Use the SaaS build-or-buy worksheet to make isolation an explicit acceptance item, review multi-tenant SaaS development, and compare the production stack and operating boundaries before requesting an architecture review.

Conclusion: prove every path denies the other tenant

Tenant isolation is credible when the same denial survives HTTP, database, jobs, cache, files, exports, providers, and support. Keep two tenants in every relevant test, execute with production-shaped roles, and make new domains fail the coverage gate until their boundary is explicit. That turns “multi-tenant” from an architectural label into repeatable release evidence.

Frequently asked questions

No. It does not cover raw queries, privileged connections, jobs, files, exports, provider callbacks, or authorization mistakes outside the model.

Sources

#SaaS #Laravel #Architecture

Read our editorial policy

Continue reading

Related articles

  1. 01

    Engineering / 6 min read

    Decide what your PWA does without a network

    Decide what your PWA does
    Cover: Decide what your PWA does
  2. 02

    Engineering / 6 min read

    Define the export before you need to leave

    Define the export before you
    Cover: Define the export before you
  3. 03

    Engineering / 10 min read

    Designing Multi-Tenant SaaS Architecture in Laravel

    Designing Multi-Tenant SaaS Architecture in
    Cover: Designing Multi-Tenant SaaS Architecture in

Building a custom system for your business?

After “Test every tenant boundary”: tell us scope, users, and integrations — we reply with a practical plan within one business day.