◀ Knowledge hub

02, Data & state

Multi tenant isolation with Supabase row level security

codeAmani Labs Engineering
Cinematic still for Multi tenant isolation with Supabase row level security

In a multi tenant system, the database should enforce the boundary

The most expensive bug in a multi tenant product is the one where tenant A sees tenant B's data. You cannot prevent it reliably by remembering to add a where tenant_id = ? clause in every query, because the one query where someone forgets is the breach. Row level security moves the boundary into the database, where forgetting is not an option.

How row level security works

With row level security enabled on a table, Postgres applies a policy to every query automatically. The policy decides which rows the current user is allowed to see or change, and there is no path around it from the application, because the database is enforcing it below the application.

alter table invoices enable row level security;

create policy tenant_isolation on invoices
  using (tenant_id = auth.tenant_id());

Now a select that forgets its tenant filter does not leak the whole table; it returns only the rows the policy allows. The security property holds even if the application code is wrong, which is exactly the property you want for the boundary that must never fail.

Pair it with managed auth

Supabase ships authentication, storage, and row level security together, so the identity that the policy reads is the same identity the user signed in with. The auth functions inside the policy come from the verified session, not from a value the client can spoof. That closing of the loop, the policy trusting only server verified identity, is what makes the isolation real rather than decorative.

Test the policy, not just the happy path

Write a test that signs in as tenant A and tries to read tenant B's rows, and assert that it gets nothing. The policy is the most important security control in the system, so it deserves a test that actively tries to defeat it. A policy nobody has attacked is a policy nobody can trust.

The principle for regulated builds

A compliance reviewer asks how you guarantee data separation. "We are careful in our queries" is not an answer. "The database refuses to return another tenant's rows, here is the policy and here is the test that proves it" is. Push the boundary down to where it cannot be bypassed, and the guarantee becomes structural.

Qualified conversation

Have a build to de-risk? Let's talk.

Tell us what you are building. We respond within two business days.