How do I protect user data with row-level security in Supabase?
Enable RLS on every table, add a policy <code>USING (auth.uid() = user_id)</code>, and make sure every insert sets user_id = auth.uid(). Postgres enforces the rest.
Why it matters
RLS is enforced in the database, not in your app code. The AI cannot forget to check permissions because the check does not live in code the AI writes.
How to do it
-
Enable RLS on the table
ALTER TABLE app_data ENABLE ROW LEVEL SECURITY; -
Add a self-only policy
CREATE POLICY "own rows" ON app_data FOR ALL USING (auth.uid() = user_id) WITH CHECK (auth.uid() = user_id); -
Set user_id on insert
Either default to auth.uid() in the column definition, or set it explicitly from the Flutter client. Both work; the column default is safer.
-
Test with two accounts
Sign in as user A, insert a row. Sign in as user B, try to read it. If B sees it, your policy is wrong.
Common mistakes
- Enabling RLS without adding any policy — locks everyone out.
- Using <code>USING (true)</code> — no protection at all.
- Forgetting the WITH CHECK clause — users can insert rows with someone else's user_id.
Frequently asked
- Does RLS slow down queries?
- A policy that references auth.uid() adds a filter — usually negligible. Complex policies with joins can slow queries; test with EXPLAIN.
- How do I let admins bypass RLS?
- Use the service_role key from server code only, or add an admin role check in the policy.