Diagnosing & Locking Down Lovable + Supabase RLS Leaks, Broken Magic Links & Exposed Keys
Lovable + Supabase applications frequently reach 80% completion with wide-open PostgreSQL tables because AI prompts disable Row-Level Security (RLS) or add permissive USING (true) policies to bypass query errors. Locking down production requires enforcing tenant-scoped auth.uid() policies, moving privileged mutations to Deno Edge Functions, and rotating leaked service-role keys.
Symptom → Root Technical Cause → Surgical Engineering Fix
| What You Experience | Why the AI Agent / App Is Stuck | Aquarel's Surgical Engineering Fix |
|---|---|---|
| Any logged-in or anonymous user can query all rows in public.profiles or public.subscriptions via browser DevTools | AI migration script ran ALTER TABLE ... DISABLE ROW LEVEL SECURITY or created a policy with USING (true) to fix an empty UI table. | Enable RLS on every public table and enforce strict `(SELECT auth.uid()) = user_id` SELECT/INSERT/UPDATE/DELETE policies. |
| Magic links or Google OAuth sign-ins redirect back to `/login` or crash on first signup | Missing `SECURITY DEFINER` trigger on `auth.users` to provision `public.profiles` atomically before client queries run. | Install an atomic PostgreSQL trigger on `auth.users` and handle PKCE code exchange cleanly on the callback route. |
| Frontend calls Stripe or admin Supabase queries using `VITE_SUPABASE_SERVICE_ROLE_KEY` | AI assistant placed the admin service_role secret into `VITE_*` client environment variables to bypass RLS restrictions. | Immediately rotate the service_role key in Supabase Dashboard and move privileged logic into verified Supabase Edge Functions. |
# Aquarel Vibe-Safe Handover: Supabase & Lovable Security Guardrails
## 1. Row-Level Security (RLS) Rules (NON-NEGOTIABLE)
- NEVER run `DISABLE ROW LEVEL SECURITY` or `USING (true)` on tenant data tables (`profiles`, `subscriptions`, `documents`).
- Every migration in `supabase/migrations/*.sql` MUST enable RLS and scope policies to `(select auth.uid()) = user_id`.
## 2. Environment Variable Isolation
- NEVER prefix `SUPABASE_SERVICE_ROLE_KEY` or `STRIPE_SECRET_KEY` with `VITE_` or `NEXT_PUBLIC_`.
- All privileged mutations and webhook handlers MUST execute inside `supabase/functions/*` Deno Edge Functions. Why AI Assistants Disable Supabase Row-Level Security
When you tell an AI coding assistant “My dashboard isn’t showing any data,” the assistant often diagnoses a Supabase permission error and fixes it by generating a migration with USING (true) or disabling RLS altogether. While that makes data appear on your screen immediately, it also allows anyone with your public anon key to read or modify every customer’s records.
Broken AI Pattern vs. Surgical Engineering Fix
- Broken AI Pattern: Prompting Lovable or Claude Code to “fix empty dashboard query” causes it to run
ALTER TABLE public.subscriptions DISABLE ROW LEVEL SECURITY;orCREATE POLICY "Allow all" ON public.profiles FOR ALL USING (true);, while embeddingVITE_SUPABASE_SERVICE_ROLE_KEYin browser code. - Surgical Engineering Fix: Re-enable RLS on every table, enforce
USING ((select auth.uid()) = user_id)andWITH CHECK ((select auth.uid()) = user_id), provision user rows via aSECURITY DEFINERtrigger onauth.users, and isolate privileged Stripe/admin mutations inside verified Deno Edge Functions.
In our 3–7 day Lovable + Supabase Rescue Sprint, we audit every table, write tenant-isolated RLS policies backed by (select auth.uid()) = user_id, move secret-key logic into Deno Edge Functions, and lock those rules into your repo’s CLAUDE.md.