Module 4
Topic 6

Row Level Security (RLS)

Protecting user data with PostgreSQL policies — the most important security feature in Supabase.

Row Level Security Policies
What Is Row Level Security?

Row Level Security (RLS) is a PostgreSQL feature that restricts which rows a user can access in a database table. It's the most important security feature in Supabase because it ensures that users can only access data they're authorized to see.

🔐 Why RLS Matters

  • Data Isolation – Users only see their own data
  • Security – Even if the API key is exposed, data is protected
  • Multi-tenant – Build apps with multiple users safely
  • Defense in Depth – Security at the database level

Without RLS

  • All users can see all data
  • Security depends on client-side code
  • Easy to accidentally expose data
  • Not suitable for multi-tenant apps

With RLS

  • Users only see their own data
  • Security at the database level
  • Data is protected even if client code is compromised
  • Essential for multi-tenant apps

⚠️ Critical: RLS Is Off by Default

In Supabase, RLS is disabled by default . You must explicitly enable it and create policies for each table. Without RLS, anyone with the anon key can read and write all data .

Types of Policies

There are four main types of policies you can create:

👀
SELECT
Control who can read data
✏️
INSERT
Control who can create new rows
🔄
UPDATE
Control who can modify data
🗑️
DELETE
Control who can remove data
Creating Policies

Policies are created using SQL in the Supabase SQL Editor or through the Dashboard UI.

🔑 Understanding Policy Syntax

  • USING – Checks if the row should be visible/accessible
  • WITH CHECK – Checks if the new row should be allowed (INSERT/UPDATE)
  • auth.uid() – Returns the current user's ID from the auth system
  • auth.role() – Returns the user's role (authenticated, anon, service_role)
Common Policy Patterns
RLS in Action

Here's how RLS works in practice with a Flutter app.

⚠️ Important: RLS Errors

If a user tries to perform an operation that violates RLS policies, Supabase will return a 403 Forbidden error. Always handle these errors gracefully in your Flutter app.

RLS with Realtime

RLS policies also apply to Realtime subscriptions . This means users will only receive updates for rows they're authorized to see.

💡 RLS + Realtime = Secure Live Data

  • Users only receive updates they're authorized to see
  • No need to filter on the client side
  • Security is enforced at the database level
Complete Example: Secure Blog App

Here's a complete blog app with RLS policies ensuring users only see and modify their own data.

Step-by-Step Explanation
1.
Enable RLS – Run ALTER TABLE table_name ENABLE ROW LEVEL SECURITY; for each table.
2.
Create SELECT policies – Define who can read data using USING conditions.
3.
Create INSERT policies – Define who can insert data using WITH CHECK conditions.
4.
Create UPDATE policies – Define who can update data using both USING and WITH CHECK .
5.
Create DELETE policies – Define who can delete data using USING conditions.
6.
Test policies – Verify that users can only access their own data.
Common Mistakes
❌ Mistake 1: Not enabling RLS

RLS is disabled by default. If you don't enable it, all data is accessible to everyone with the anon key.

✅ Correct: Always enable RLS

Run ALTER TABLE table_name ENABLE ROW LEVEL SECURITY; for every table that contains user data.

❌ Mistake 2: Creating policies without checking auth.uid()

Policies that don't check auth.uid() may expose data to unauthorized users.

✅ Correct: Always check auth.uid()

Use USING (auth.uid() = user_id) or similar conditions to ensure data isolation.

❌ Mistake 3: Not handling RLS errors in Flutter

When RLS blocks an operation, Supabase returns a 403 error. Your app must handle this gracefully.

✅ Correct: Handle RLS errors

Use try-catch blocks and show user-friendly messages when RLS blocks an operation.

🎯 Key Takeaway

Row Level Security is the most critical security feature in Supabase. Always enable RLS and create policies for each table . Use auth.uid() to ensure users can only access their own data. RLS protects your data even if client-side code is compromised.