r/Supabase•
asggjjd

The RLS insert policy mistake that lets any user join any organization

database

I've been building a multi-tenant app on Supabase and audited my policies before shipping. Found a bug worth sharing, because I suspect it's fairly common.

The membership insert policy looked like this:

create policy "users can insert their own membership"
on organization_members for insert
with check (user_id = auth.uid());

Read it as a sentence: you may add a membership row as long as the row is about you. It constrains who is being added and says nothing at all about which organization they're being added to. Org ids aren't secret, they show up in URLs, API responses and invite links.

await supabase.from('organization_members').insert({
  organization_id: someOtherOrgId,
  user_id: myUserId,
  role: 'owner',
})

The policy passes, because user_id really is auth.uid(). They're now an owner of a tenant they have no relationship to.

The part that made it worse than I first thought: every SELECT policy in my schema keys off membership, which was the whole design. So the moment an attacker holds a membership row, those policies start working for them. The read policies were never wrong, they were just asked the wrong question.

Fix is to constrain the tenant, not just the actor:

with check (
  user_id = auth.uid()
  and role = 'owner'
  and organization_id in (select id from organizations where owner_id = auth.uid())
);

Two testing gotchas I hit while writing tests for this:

Superusers bypass RLS entirely. If your policy tests connect as postgres or the table owner, they pass no matter how broken the policy is. Tests have to run as a non-superuser through the same auth.uid() path the app uses.

Denials are usually silent. A blocked SELECT returns zero rows rather than throwing, and a blocked DELETE reports success having removed nothing. Only WITH CHECK violations raise. So asserting "it didn't error" tests nothing, you have to read the data back and assert on what's actually there.

Full write-up with the fix and the test suite: https://keystone-landing-cyy.pages.dev/guides/supabase-rls-tenant-isolation/

Disclosure: I sell a starter kit built on these patterns. The guide stands alone though, no signup wall or email gate.

Posted Sep 1, 2026, 4:35 PM UTC

113
Loading comments…