Overview
EpiNeko uses PostgreSQL Row Level Security (RLS) to ensure users can only access and modify their own data. All tables have RLS enabled with specific policies for different operations.RLS policies are defined in
supabase/migrations/20260218_initial_schema.sql:13 and enforce security at the database level.Why RLS?
Row Level Security provides database-level security that:- Cannot be bypassed - Even if application code has bugs, RLS protects data
- Works automatically - No need to add WHERE clauses to every query
- Leverages Supabase Auth - Uses
auth.uid()to identify current user - Prevents data leaks - Users can only see their own data
profiles Table Policies
SELECT Policy: Public profiles are viewable by everyone
string
Public profiles are viewable by everyone.
SELECT
Applies to SELECT queries
using (true)
Always returns true - all profiles are publicly visible
INSERT Policy: Users can insert their own profile
string
Users can insert their own profile.
INSERT
Applies to INSERT queries
with check (auth.uid() = id)
User can only insert if the profile
id matches their authenticated auth.uid()handle_new_user() trigger creates profiles automatically, so this policy rarely applies in practice.
UPDATE Policy: Users can update own profile
string
Users can update own profile.
UPDATE
Applies to UPDATE queries
using (auth.uid() = id)
User can only update rows where
id matches their auth.uid()user_library Table Policies
SELECT Policy: Users can view their own library items
string
Users can view their own library items.
SELECT
Applies to SELECT queries
using (auth.uid() = user_id)
User can only select rows where
user_id matches their auth.uid()INSERT Policy: Users can insert their own library items
string
Users can insert their own library items.
INSERT
Applies to INSERT queries
with check (auth.uid() = user_id)
User can only insert if
user_id matches their auth.uid()UPDATE Policy: Users can update their own library items
string
Users can update their own library items.
UPDATE
Applies to UPDATE queries
using (auth.uid() = user_id)
User can only update rows where
user_id matches their auth.uid()DELETE Policy: Users can delete their own library items
string
Users can delete their own library items.
DELETE
Applies to DELETE queries
using (auth.uid() = user_id)
User can only delete rows where
user_id matches their auth.uid()How RLS Works
Authentication Context
RLS policies useauth.uid() to get the current user’s ID from the JWT token:
- Authenticated users:
auth.uid()returns their UUID - Unauthenticated users:
auth.uid()returnsNULL
Policy Evaluation
PostgreSQL evaluates RLS policies for every query:- SELECT: Check
USINGclause - if false, row is filtered out - INSERT: Check
WITH CHECKclause - if false, insert fails - UPDATE: Check
USINGclause - if false, row cannot be updated - DELETE: Check
USINGclause - if false, row cannot be deleted
Example Flow
Testing RLS Policies
Test as Different Users
Using the Supabase Dashboard
- Go to SQL Editor in Supabase Dashboard
- Use
auth.uid()to test as specific users:
Bypassing RLS (Admin Operations)
Service Role Key
Use the service role key for operations that need to bypass RLS:Security Definer Functions
Create database functions withSECURITY DEFINER to run with elevated privileges:
Common Issues
Best Practices
Policy Summary
Related Resources
Database Schema
View complete database schema
Supabase Integration
How to use Supabase clients
Library Service
High-level library operations with RLS
Supabase RLS Docs
Official RLS documentation