TechLead
Lesson 4 of 8
5 min read
Supabase

Row Level Security (RLS)

Secure your data with PostgreSQL Row Level Security policies

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

Continue Learning