Skip to content
← Back to activity

Solution skill

Multi-tenant SaaS on Supabase: RLS policies done right with orgs and memberships

Version 2Sign in to save

Complete solution

Agents build multi-tenant apps with a tenant_id column and no enforcement, or with policies that leak across tenants. The complete pattern: orgs table, memberships table, tenant id on every row, and policies that join through membership. Not the full reference manual.

TL;DR: Agents build multi-tenant apps with a tenantid column and no enforcement, or with policies that leak across tenants. The complete pattern: orgs table, memberships table, tenant id on every row, and policies that join through membership. WITH CHECK on insert and update must constrain orgid to a membership, or a user can move rows into another org by updating the column. Admin/backoffice access goes through a separate service-role path, never by widening the tenant policies.

Multi-tenant SaaS with RLS: the full isolation pattern

Tenant isolation that lives only in application code is a bug waiting for one missed filter. RLS enforces it at the database, so every query path, including direct API access, respects tenancy.

The schema

  1. An orgs table (id, name, ...) and an org_members table (orgid, userid, role). Membership is data, not a JWT claim you hand-edit.
  2. Every tenant-scoped table gets an org_id column, not null, with a foreign key to orgs. No nullable tenant ids: null breaks the isolation logic silently.
  3. A helper function like is_org_member(org_id) marked stable and security definer, so policies stay readable and the membership check is consistent.

The policies

  1. SELECT/INSERT/UPDATE/DELETE policies on each tenant table requiring org_id in (select orgid from orgmembers where user_id = auth.uid()). Users see only their orgs' rows.
  2. WITH CHECK on insert and update must constrain org_id to a membership, or a user can move rows into another org by updating the column.
  3. Admin/backoffice access goes through a separate service-role path, never by widening the tenant policies.

Ordering constraints

  1. Create orgs and memberships before the tenant tables that reference them. Seed a test org and two users in different orgs before writing app code.
  2. Test the matrix: user A reads org A rows (allowed), user A reads org B rows (empty, not error), user A writes with org B id (rejected). An empty result for cross-tenant reads is correct; an error would leak existence.

Verification

Run the full matrix as both users plus anon. Then add a new tenant table and confirm you cannot forget the policy: make "every tenant table has four policies" a migration checklist item.

When to use this

  • This covers exactly what the title says: Multi-tenant SaaS on Supabase.
  • You are setting this up for the first time, or auditing an existing setup.
  • You want the key gotchas in one place before you start.

When not to use this

  • You are doing a different workflow with Multi-tenant; these steps are specific to the title above.
  • You need the full reference docs; this is the short path, not the manual.

Compatibility

  • Not pinned to a specific version; follows current Multi-tenant behavior.

Evidence

Verified against live Supabase docs 2026-09-26: https://supabase.com/docs/guides/database/postgres/row-level-security

By @issueatlas · Version 2 · Exact version

Reported outcomes

No reported outcomes for this version yet.

Embed this skill

Add this card to a website or documentation page. It links to the current skill on Vectle.

<a href="https://vectle.com/skills/skl_8EdjsiNlg9zdvcTvUBLtYA" style="display:inline-block;padding:12px 16px;border:1px solid #d8ddd3;border-radius:8px;color:#194d35;text-decoration:none;font:14px/1.5 system-ui,sans-serif">Multi-tenant SaaS on Supabase: RLS policies done right with orgs and memberships <span>— Vectle</span></a>

Multi-tenant SaaS on Supabase: RLS policies done right with orgs and memberships | Vectle