2026-08-20 · 8 min read
How to fix Supabase RLS so Lovable users cannot see each other's data
The usual Lovable + Supabase leak: RLS off, policies that use true, or queries that skip auth.uid(). How I diagnose and close it before launch.
Most Lovable apps I get handed “almost work” until a second user signs up. Then user B sees user A’s rows — chats, invoices, leads, whatever. That is almost never a Lovable UI bug. It is Supabase Row Level Security missing, disabled, or written as USING (true).
#What “broken RLS” looks like in practice
- Table exists, frontend selects * from it, and every logged-in user gets every row.
- Auth works (you see a session), but policies were never enabled on the table.
- A policy exists but uses true, or checks a column that the insert never sets (like user_id).
- The Lovable client uses the anon key correctly, but the table was tested only with the service role in the SQL editor — which bypasses RLS.
#Step 1 — Confirm RLS is actually on
In the Supabase SQL editor, check the table. If rowsecurity is false, nothing you write in the frontend will protect you.
select relname, relrowsecurity
from pg_class
where relname = 'leads';
alter table public.leads enable row level security;#Step 2 — Own every row with auth.uid()
Every user-owned table needs a column that maps to the signed-in user, usually user_id uuid references auth.users(id). Inserts from Lovable must set that column from the session — not from a free-text field the client invents.
-- Example: each user only sees their own leads
create policy "leads_select_own"
on public.leads
for select
to authenticated
using (auth.uid() = user_id);
create policy "leads_insert_own"
on public.leads
for insert
to authenticated
with check (auth.uid() = user_id);
create policy "leads_update_own"
on public.leads
for update
to authenticated
using (auth.uid() = user_id)
with check (auth.uid() = user_id);
create policy "leads_delete_own"
on public.leads
for delete
to authenticated
using (auth.uid() = user_id);#Step 3 — Test with two real accounts, not the SQL editor
Create user A and user B in Auth. Sign in as A in the Lovable app, insert a row, then sign in as B. If B can read A’s row, the policy is wrong or the insert wrote the wrong user_id. Do not trust “it worked when I ran select * as postgres.”
#Common Lovable-specific traps
- Generated CRUD that never passes user_id — fix the insert payload or a database default: user_id uuid default auth.uid().
- Using the service role key in the browser (never). Keep service role on the server only.
- Realtime channels subscribed without filters — still enforce RLS; do not assume channel names equal security.
- Admin dashboards that need to see all rows — use a separate role + policies, or a server route with the service role, not USING (true) for everyone.
#When to stop DIY and get it audited
If you already have production users, or multi-tenant orgs (org_id + membership tables), stop patching one policy at a time. I audit the schema, rewrite policies, and verify with role-based tests before you keep shipping features on a leaky base. That is the bulk of Lovable + Supabase rescue work I do.