Tenant isolation

Tenant isolation must survive background work

Filtering an API request by organisation is not enough if jobs, caches or exports lose that context. Carry the boundary through every path that reads or changes tenant data.

In this article

The request can be correct while the job is wrong

A user in tenant A requests an invoice export. The API verifies membership and queues a job containing only the invoice identifier. The worker later loads that identifier without tenant scope and returns a record belonging to tenant B.

The initial access check worked. The failure occurred when the application crossed from an authenticated request into background work. Tenant identity needs to remain explicit wherever the task continues, and the worker must enforce the appropriate authority rather than assuming the queue is inherently safe.

A tenant identifier supplied by a client is not proof of membership. Resolve the active tenant through trusted authentication and authorisation before creating the work item.

Preserve scope in data and operation contracts

Use tenant scope in lookups, mutations, cache keys and operation identities where the data model requires it. A globally unique record identifier reduces accidental collisions, but it does not replace access checks.

For a queued job, save the tenant, target, requested operation and authority context needed for later validation. Decide whether the job executes under the requester's current rights or an explicitly authorised organisation-owned process. Long-running work can outlive the original session.

An export keeps its tenant boundaryTrusted tenant context follows the request into queued work, scoped data access and the final download.
  1. Authenticated requestResolve membership and the permitted tenant
  2. Job recordPersist tenant, target and operation scope
  3. WorkerRevalidate authority and query within that tenant
  4. Export accessStore and serve the result under the same boundary

Choose enforcement layers deliberately

Application checks provide business context. Database controls can add protection where the platform and schema support them. PostgreSQL row-level security, for example, can constrain ordinary row access, but roles with bypass privileges and table ownership require careful review.

Test with the identity the application actually uses. A policy demonstrated under a restricted test role says little if production connects as a role that bypasses it.

Keep connection-pool behaviour in scope. Any session-level tenant context must be established and cleared safely according to the pool and transaction model. A reused connection should not inherit another request's tenant.

Include derived and operational data

Exports, search indexes, saved answers and logs can contain tenant information outside the primary tables. Apply the same ownership model to those copies and their access endpoints.

Cache keys need enough scope to prevent one tenant's result being reused for another. Shared operational dashboards should expose only the data appropriate to their audience, with a separate controlled path for support investigations.

Test the paths people forget

Create similar records in two tenants and exercise direct reads, queued jobs, retries, exports and administrative tools. Pause a job, change access and resume it under the documented policy.

Inspect both response content and actual mutations. A denied final download does not undo a worker that already wrote data into the wrong tenant's storage. Isolation is dependable when it survives the complete workflow, including the parts that run after the original HTTP request has ended.

Primary sources

OWASP: multitenant securityPostgreSQL: row security policies

References checked 11 September 2026.