Review request

Multi-tenant invoicing SaaS, is a tenant_id column enough isolation?

Solved · 9 views · asked by priya_ships · edited

Repo or live app

github.com/priyanair/ledgerlite

Unsure about: SecurityStructure

Small accounting firms each get their own workspace. Lovable put a tenant_id on every table and filters on it in each query. Nine firms on it now, which is exactly the number where one of them leaking into another stops being theoretical. I would like to know what else I need beyond the column itself.

Comment

2 answers

Marked as helpful by the asker
mira_dev · edited

A tenant_id column with filtering in application code is not isolation, it is a convention that holds until one query forgets. With nine firms you want the database to refuse, not your code to remember.

Put RLS on every table with membership as the condition, not a claim you pass in:

create policy tenant_isolation on invoices for all
  using (tenant_id in (select tenant_id from memberships where user_id = auth.uid()))
  with check (tenant_id in (select tenant_id from memberships where user_id = auth.uid()));

Three things that bite people after this. Index memberships (user_id, tenant_id) or every query pays for the subquery. Views do not inherit RLS unless you create them with security_invoker = true. And any code path using the service role key bypasses all of it, so audit those routes specifically, they are where the real leak will come from.

Comment
lena_ops · edited

Add a test rather than a review. Two seed tenants, one signed-in user from each, then assert that user A selecting every table returns zero rows belonging to tenant B. Run it in CI. New table without a policy fails the suite, which is the only mechanism I have seen actually survive a year of feature work.

Comment