# 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.

By Cobnex editorial. Published 2026-09-10. Updated 2026-09-11.

## 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 boundary

Trusted tenant context follows the request into queued work, scoped data access and the final download.

1. **Authenticated request**: Resolve membership and the permitted tenant
2. **Job record**: Persist tenant, target and operation scope
3. **Worker**: Revalidate authority and query within that tenant
4. **Export access**: Store 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.

## Sources

- [OWASP: multitenant security](https://cheatsheetseries.owasp.org/cheatsheets/Multi_Tenant_Security_Cheat_Sheet.html)
- [PostgreSQL: row security policies](https://www.postgresql.org/docs/17/ddl-rowsecurity.html)
