Row Level Security
La sécurité au niveau de chaque ligne : chaque utilisateur ne voit que ses propres données.
5.1Pourquoi RLS est indispensable
Ta anon key est publique (visible dans le code client). Sans RLS, voici ce qui se passe :
Sans RLS
- - N'importe qui peut lire toutes les données
- - N'importe qui peut modifier les données des autres
- - N'importe qui peut supprimer ce qu'il veut
- - Ta base est un buffet à volonté
Avec RLS
- - Chaque utilisateur ne voit que ses données
- - Impossible de modifier les données d'un autre
- - Les policies sont évaluées à chaque requête
- - La sécurité est dans la base, pas dans ton code
5.2Activer RLS
Activer RLS sur une tablebash
-- TOUJOURS activer RLS sur les tables publiques
ALTER TABLE public.articles ENABLE ROW LEVEL SECURITY;
-- Par défaut, une fois RLS activé, PERSONNE n'a accès
-- (même pas l'utilisateur connecté)
-- Il faut créer des policies pour autoriser les opérations5.3Créer des policies
Une policy RLS est une règle SQL qui dit : "qui peut faire quoi sur quelles lignes".
Policy : chacun lit ses propres articlesbash
CREATE POLICY "Users can read own articles"
ON public.articles
FOR SELECT
USING (auth.uid() = author_id);
-- auth.uid() = l'ID de l'utilisateur connecté (extrait du JWT)
-- Cette policy dit : "tu ne peux SELECT que les lignes
-- où author_id correspond à ton ID"Policy : chacun insère pour lui-mêmebash
CREATE POLICY "Users can insert own articles"
ON public.articles
FOR INSERT
WITH CHECK (auth.uid() = author_id);
-- WITH CHECK valide les données AVANT l'insertion
-- Empêche un utilisateur d'insérer avec l'ID d'un autrePolicy : chacun modifie/supprime les siensbash
CREATE POLICY "Users can update own articles"
ON public.articles
FOR UPDATE
USING (auth.uid() = author_id)
WITH CHECK (auth.uid() = author_id);
CREATE POLICY "Users can delete own articles"
ON public.articles
FOR DELETE
USING (auth.uid() = author_id);5.4Patterns courants de policies
Pattern 1 : Tout le monde peut lire, seul l'auteur modifiebash
-- Articles publiés : lecture publique
CREATE POLICY "Public can read published articles"
ON public.articles
FOR SELECT
USING (published = true);
-- Auteur : CRUD complet sur ses propres articles
CREATE POLICY "Authors can manage own articles"
ON public.articles
FOR ALL
USING (auth.uid() = author_id)
WITH CHECK (auth.uid() = author_id);Pattern 2 : Lecture pour les utilisateurs connectésbash
-- Toute personne connectée peut lire
CREATE POLICY "Authenticated users can read"
ON public.articles
FOR SELECT
TO authenticated -- Rôle Supabase : 'authenticated' = connecté
USING (true);Pattern 3 : Insertion côté serveur uniquementbash
-- Les visiteurs ne doivent jamais écrire directement dans cette table
ALTER TABLE public.contact_messages ENABLE ROW LEVEL SECURITY;
-- Aucune policy INSERT pour 'anon' ou 'authenticated'.
-- Une route serveur valide les données puis utilise la clé service_role.
-- Cette clé reste secrète et n'est jamais envoyée au navigateur.5.5USING vs WITH CHECK
| Clause | Quand | Usage |
|---|---|---|
| USING | SELECT, UPDATE, DELETE | Filtre les lignes existantes auxquelles tu as accès |
| WITH CHECK | INSERT, UPDATE | Valide les nouvelles données avant écriture |
5.6Exemple complet : une plateforme d'apprentissage
Trois tables personnelles protégées par RLSbash
-- Table progression
CREATE TABLE public.chapter_progress (
user_id UUID REFERENCES auth.users(id) NOT NULL,
course_id TEXT NOT NULL,
chapter_slug TEXT NOT NULL,
completed_at TIMESTAMPTZ,
PRIMARY KEY (user_id, course_id, chapter_slug)
);
ALTER TABLE public.chapter_progress ENABLE ROW LEVEL SECURITY;
CREATE POLICY "rw_own_progress" ON public.chapter_progress FOR ALL
USING (auth.uid() = user_id)
WITH CHECK (auth.uid() = user_id);
-- Table résultats des QCM
CREATE TABLE public.chapter_quiz_results (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
user_id UUID REFERENCES auth.users(id) NOT NULL,
course_id TEXT NOT NULL,
chapter_slug TEXT NOT NULL,
score INTEGER NOT NULL,
total_questions INTEGER NOT NULL,
UNIQUE(user_id, course_id, chapter_slug)
);
ALTER TABLE public.chapter_quiz_results ENABLE ROW LEVEL SECURITY;
CREATE POLICY "rw_own_quiz" ON public.chapter_quiz_results FOR ALL
USING (auth.uid() = user_id)
WITH CHECK (auth.uid() = user_id);
-- Table auto-évaluation
CREATE TABLE public.chapter_feedback (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
user_id UUID REFERENCES auth.users(id) NOT NULL,
course_id TEXT NOT NULL,
chapter_slug TEXT NOT NULL,
rating TEXT NOT NULL,
UNIQUE(user_id, course_id, chapter_slug)
);
ALTER TABLE public.chapter_feedback ENABLE ROW LEVEL SECURITY;
CREATE POLICY "rw_own_feedback" ON public.chapter_feedback FOR ALL
USING (auth.uid() = user_id)
WITH CHECK (auth.uid() = user_id);