supabase-rls-policy
Supabase RLS Policy Expert
Generate production-ready row-level security policies for Supabase PostgreSQL databases following best practices and Supabase-specific conventions.
Core Policy Syntax
Policy Structure
All policies follow this structure:
CREATE POLICY "Policy description" ON table_name
FOR operation
TO role
USING (condition)
WITH CHECK (condition);
Operations and Conditions
- SELECT: Use USING only, no WITH CHECK
- INSERT: Use WITH CHECK only, no USING
- UPDATE: Use both USING and WITH CHECK
- DELETE: Use USING only, no WITH CHECK
Never use FOR ALL - always create separate policies for each operation (SELECT, INSERT, UPDATE, DELETE).
Supabase-Specific Features
Authentication Roles
Supabase maps requests to two built-in roles:
anon: Unauthenticated users (not logged in)authenticated: Authenticated users (logged in)
Apply roles with the TO clause, which must come after the operation:
-- CORRECT
CREATE POLICY "policy name" ON profiles
FOR select
TO authenticated
USING (true);
-- INCORRECT - TO must come after FOR
CREATE POLICY "policy name" ON profiles
TO authenticated
FOR select
USING (true);
Helper Functions
auth.uid() - Returns the ID of the authenticated user making the request
auth.jwt() - Returns the JWT with access to user metadata:
raw_user_meta_data: User-updatable, not secure for authorizationraw_app_meta_data: Cannot be updated by user, use for authorization
Example using JWT for team membership:
CREATE POLICY "User is in team" ON my_table
TO authenticated
USING (team_id IN (
SELECT auth.jwt() -> 'app_metadata' -> 'teams'
));
MFA Requirements
Check for multi-factor authentication using AAL (Assurance Level):
CREATE POLICY "Restrict updates" ON profiles
AS restrictive
FOR update
TO authenticated
USING ((SELECT auth.jwt()->>'aal') = 'aal2');
Performance Optimization
Critical Optimizations
- Add indexes on columns used in policies:
CREATE INDEX userid ON test_table USING btree (user_id);
- Wrap functions in SELECT to enable caching:
-- OPTIMIZED - uses initPlan caching
CREATE POLICY "policy" ON test_table
TO authenticated
USING ((SELECT auth.uid()) = user_id);
-- SLOWER - calls function on every row
CREATE POLICY "policy" ON test_table
TO authenticated
USING (auth.uid() = user_id);
- Minimize joins - fetch criteria into sets instead:
-- SLOW - joins on each row
CREATE POLICY "Team access" ON test_table
TO authenticated
USING (
(SELECT auth.uid()) IN (
SELECT user_id FROM team_user
WHERE team_user.team_id = team_id -- JOIN
)
);
-- FAST - no join
CREATE POLICY "Team access" ON test_table
TO authenticated
USING (
team_id IN (
SELECT team_id FROM team_user
WHERE user_id = (SELECT auth.uid()) -- no join
)
);
- Always specify roles with
TOclause:
-- OPTIMIZED
CREATE POLICY "policy" ON rls_test
TO authenticated
USING ((SELECT auth.uid()) = user_id);
Syntax Rules
String Handling
Always use double apostrophes in SQL strings:
-- CORRECT
name = 'Night''s watch'
-- INCORRECT
name = 'Night\'s watch'
Multiple Operations
Create separate policies for each operation - PostgreSQL doesn't support multiple operations per policy:
-- INCORRECT
CREATE POLICY "policy" ON profiles
FOR insert, delete -- NOT SUPPORTED
TO authenticated
WITH CHECK (true)
USING (true);
-- CORRECT
CREATE POLICY "Can create profiles" ON profiles
FOR insert
TO authenticated
WITH CHECK (true);
CREATE POLICY "Can delete profiles" ON profiles
FOR delete
TO authenticated
USING (true);
Policy Patterns
Owner-Based Access
-- Users can view their own records
CREATE POLICY "Users view own records" ON test_table
FOR select
TO authenticated
USING ((SELECT auth.uid()) = user_id);
-- Users can update their own records
CREATE POLICY "Users update own records" ON test_table
FOR update
TO authenticated
USING ((SELECT auth.uid()) = user_id)
WITH CHECK ((SELECT auth.uid()) = user_id);
Team-Based Access
-- Users can access team records
CREATE POLICY "Team member access" ON test_table
FOR select
TO authenticated
USING (
team_id IN (
SELECT team_id FROM team_user
WHERE user_id = (SELECT auth.uid())
)
);
Public Read, Authenticated Write
-- Anyone can read
CREATE POLICY "Public read" ON profiles
FOR select
TO anon, authenticated
USING (true);
-- Only authenticated can insert
CREATE POLICY "Authenticated insert" ON profiles
FOR insert
TO authenticated
WITH CHECK (true);
Policy Types
PERMISSIVE (Default, Recommended)
Policies are combined with OR - if any policy grants access, it's allowed. Always prefer PERMISSIVE unless you have a specific need for RESTRICTIVE.
RESTRICTIVE (Use Sparingly)
All RESTRICTIVE policies must pass (AND logic). Use only for additional security layers like MFA requirements. Discourage use because:
- More complex to reason about
- Can accidentally lock out users
- Harder to debug access issues
Output Format
Always wrap SQL in markdown code blocks with language tag:
CREATE POLICY "Descriptive policy name" ON books
FOR insert
TO authenticated
WITH CHECK ((SELECT auth.uid()) = author_id);
Policy naming: Use descriptive sentences in double quotes explaining what the policy does.
Explanations: Provide as separate text, never inline SQL comments.
Validation Checklist
Before finalizing policies, verify:
- ✓ Used
auth.uid()instead ofcurrent_user - ✓ Wrapped functions in
SELECTfor performance - ✓ Added indexes on policy columns
- ✓ Specified roles with
TOclause - ✓ Minimized joins in policy logic
- ✓ Used correct USING/WITH CHECK for operation type
- ✓ Created separate policies per operation (no
FOR ALL) - ✓ Used double apostrophes in strings
- ✓ Used PERMISSIVE unless RESTRICTIVE required
- ✓ Provided clear policy descriptions
Out of Scope
If user requests anything not related to RLS policies, explain that this skill only assists with Supabase row-level security policy creation and suggest they rephrase their request or use other tools.
More from linehaul-ai/linehaulai-claude-marketplace
geospatial-postgis-patterns
Implement geofences, spatial queries, real-time tracking, and mapping features in laneweaverTMS using PostGIS and PGRouting. Use when building location-based features, distance calculations, ETA predictions, or fleet visualization.
83quickbooks-online-api
Expert guide for QuickBooks Online API integration covering authentication, CRUD operations, batch processing, and best practices for invoicing, payments, and customer management.
61rbac-authorization-patterns
Provide patterns for implementing Role-Based Access Control and multi-tenant authorization in laneweaverTMS. Use when implementing user roles, permissions, tenant isolation, Echo authorization middleware, RLS policies for multi-tenant access, or JWT claims structure for freight brokerage applications.
61slack-block-kit
Build Slack Block Kit UIs for messages, modals, and Home tabs. Use when creating Slack notifications, interactive forms, bot responses, app dashboards, or any Slack UI. Covers blocks (Section, Actions, Input, Header), elements (Buttons, Selects, Date pickers), composition objects, and the slack-block-builder library.
44svelte-flow
Build node-based editors, interactive diagrams, and flow visualizations using Svelte Flow. Use when creating workflow editors, data flow diagrams, organizational charts, mindmaps, process visualizations, DAG editors, or any interactive node-graph UI. Supports custom nodes/edges, layouts (dagre, hierarchical), animations, and advanced features like proximity connect, floating edges, and contextual zoom.
34testcontainers-go
Use this skill when writing Go integration tests with Docker containers, using testcontainers-go modules (postgres, redis, kafka, etc.), setting up container-based test infrastructure, or configuring container networking and wait strategies. Covers 62+ pre-configured modules, cleanup patterns, and multi-container setups.
34