What is Row Level Security?
Row Level Security (RLS) is a PostgreSQL feature that restricts which rows users can access or modify. It's Supabase's primary security mechanism, allowing you to write policies that control data access at the database level.
🛡️ Why RLS Matters
- Database-level security: Rules enforced regardless of how data is accessed
- Fine-grained control: Different rules for different operations
- Safe anon key: Enables safe client-side database access
- No backend needed: Security without server-side code
Enabling RLS
-- Enable RLS on a table
ALTER TABLE posts ENABLE ROW LEVEL SECURITY;
-- Tables with RLS enabled deny all access by default
-- You must create policies to allow access
⚠️ Important
When RLS is enabled, ALL access is denied by default. You must create policies to explicitly allow the access you want.
Creating Policies
Basic Policy Structure
CREATE POLICY "policy_name"
ON table_name
FOR operation -- SELECT, INSERT, UPDATE, DELETE, or ALL
TO role -- authenticated, anon, or specific role
USING (condition) -- For SELECT/UPDATE/DELETE
WITH CHECK (condition); -- For INSERT/UPDATE
Common Policies
Allow users to read their own data:
CREATE POLICY "Users can read own posts"
ON posts FOR SELECT
TO authenticated
USING (auth.uid() = user_id);
Allow users to insert their own data:
CREATE POLICY "Users can create posts"
ON posts FOR INSERT
TO authenticated
WITH CHECK (auth.uid() = user_id);
Allow users to update their own data:
CREATE POLICY "Users can update own posts"
ON posts FOR UPDATE
TO authenticated
USING (auth.uid() = user_id)
WITH CHECK (auth.uid() = user_id);
Allow users to delete their own data:
CREATE POLICY "Users can delete own posts"
ON posts FOR DELETE
TO authenticated
USING (auth.uid() = user_id);
Public vs Authenticated Access
-- Allow anyone to read published posts
CREATE POLICY "Public can read published posts"
ON posts FOR SELECT
TO anon, authenticated
USING (published = true);
-- Only authenticated users can read drafts
CREATE POLICY "Auth users can read own drafts"
ON posts FOR SELECT
TO authenticated
USING (auth.uid() = user_id AND published = false);
Helper Functions
-- auth.uid() - Returns the current user's ID
-- auth.jwt() - Returns the current user's JWT claims
-- auth.role() - Returns the current user's role
-- Example: Check if user is admin
CREATE POLICY "Admins can do anything"
ON posts FOR ALL
TO authenticated
USING ((auth.jwt() ->> 'role') = 'admin');
Complete Example
-- Create table
CREATE TABLE posts (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
user_id UUID REFERENCES auth.users(id) NOT NULL,
title TEXT NOT NULL,
content TEXT,
published BOOLEAN DEFAULT false
);
-- Enable RLS
ALTER TABLE posts ENABLE ROW LEVEL SECURITY;
-- Policies
CREATE POLICY "Anyone can read published posts"
ON posts FOR SELECT USING (published = true);
CREATE POLICY "Users can read own posts"
ON posts FOR SELECT
TO authenticated
USING (auth.uid() = user_id);
CREATE POLICY "Users can create own posts"
ON posts FOR INSERT
TO authenticated
WITH CHECK (auth.uid() = user_id);
CREATE POLICY "Users can update own posts"
ON posts FOR UPDATE
TO authenticated
USING (auth.uid() = user_id)
WITH CHECK (auth.uid() = user_id);
CREATE POLICY "Users can delete own posts"
ON posts FOR DELETE
TO authenticated
USING (auth.uid() = user_id);
💡 Key Takeaways
- • RLS provides database-level security for your data
- • Enable RLS on every table that stores user data
- • Use auth.uid() to match rows to the current user
- • USING filters rows for SELECT/UPDATE/DELETE
- • WITH CHECK validates data for INSERT/UPDATE
📚 Learn More
-
RLS Documentation →
Complete guide to Row Level Security in Supabase.
-
Managing User Data →
Best practices for securing user data.