Supabase Row Level Security: Komplett guide for sikre Next.js-apper
> TL;DR
Row Level Security (RLS) i Supabase er en PostgreSQL-funksjon som kontrollerer datatilgang på radnivå. Hver tabell får policies som definerer hvem som kan lese, skrive, oppdatere og slette data. Med RLS trenger du ikke bygge autorisasjonslogikk i applikasjonen — databasen håndhever reglene selv. Det er obligatorisk for alle tabeller i en Supabase-app som er eksponert mot klienten.
De fleste sikkerhetsbrudd i webapplikasjoner skyldes ikke sofistikerte angrep. De skyldes at noen glemte å sjekke om brukeren faktisk har tilgang til dataen de prøver å hente.
Row Level Security (RLS) i Supabase løser dette problemet på det mest fundamentale nivået: i selve databasen. Det er en grunnleggende del av Supabase Auth-oppsettet vi anbefaler.
Hva er Row Level Security?
RLS er en PostgreSQL-funksjon som lar deg definere hvem som kan se og manipulere hvilke rader i en tabell. I stedet for å skrive if (user.id === row.owner_id) i hver eneste API-rute, definerer du regelen én gang i databasen.
Uten RLS (usikkert)
// API-rute uten RLS — du MÅ huske å sjekke tilgang
app.get('/api/documents', async (req, res) => {
const documents = await db.query('SELECT * FROM documents WHERE owner_id = $1', [req.user.id]);
// Hva om du glemmer WHERE-leddet? All data lekker.
res.json(documents);
});
Med RLS (sikkert)
-- Databasen håndhever reglene — umulig å glemme
CREATE POLICY "Users can only see own documents"
ON documents FOR SELECT
USING (owner_id = auth.uid());
Med RLS aktivert kan klienten kalle supabase.from('documents').select('*') — og bare se sine egne dokumenter. Databasen filtrerer automatisk.
Hvordan sette opp RLS i Supabase
Steg 1: Aktiver RLS på tabellen
ALTER TABLE documents ENABLE ROW LEVEL SECURITY;
Viktig: Når RLS er aktivert uten policies, har ingen tilgang til tabellen. Du må eksplisitt definere hvem som kan gjøre hva.
Steg 2: Definer policies
Det finnes fire operasjoner: SELECT, INSERT, UPDATE, DELETE.
-- Brukere kan lese egne dokumenter
CREATE POLICY "select_own_documents"
ON documents FOR SELECT
USING (owner_id = auth.uid());
-- Brukere kan opprette dokumenter (og blir automatisk eier)
CREATE POLICY "insert_own_documents"
ON documents FOR INSERT
WITH CHECK (owner_id = auth.uid());
-- Brukere kan oppdatere egne dokumenter
CREATE POLICY "update_own_documents"
ON documents FOR UPDATE
USING (owner_id = auth.uid())
WITH CHECK (owner_id = auth.uid());
-- Brukere kan slette egne dokumenter
CREATE POLICY "delete_own_documents"
ON documents FOR DELETE
USING (owner_id = auth.uid());
Steg 3: Test det
// Som bruker A
const { data } = await supabase.from('documents').select('*');
// Returnerer KUN bruker A sine dokumenter
// Forsøk på å lese bruker B sine dokumenter
const { data: other } = await supabase.from('documents').select('*').eq('owner_id', 'bruker-b-id');
// Returnerer tomt — RLS blokkerer
Vanlige RLS-mønstre
Team-basert tilgang
-- Brukere kan se dokumenter tilhørende sitt team
CREATE POLICY "team_select"
ON documents FOR SELECT
USING (
team_id IN (
SELECT team_id FROM team_members
WHERE user_id = auth.uid()
)
);
Rollebasert tilgang
-- Admin kan se alt, vanlige brukere ser kun egne
CREATE POLICY "role_based_select"
ON documents FOR SELECT
USING (
owner_id = auth.uid()
OR
EXISTS (
SELECT 1 FROM user_roles
WHERE user_id = auth.uid()
AND role = 'admin'
)
);
Offentlig lesbart, privat skrivbart
-- Alle kan lese publiserte dokumenter
CREATE POLICY "public_read"
ON documents FOR SELECT
USING (published = true OR owner_id = auth.uid());
-- Kun eier kan skrive
CREATE POLICY "owner_write"
ON documents FOR INSERT
WITH CHECK (owner_id = auth.uid());
Vanlige feil med RLS
1. Glemmer å aktivere RLS
Uten ENABLE ROW LEVEL SECURITY er tabellen helt åpen. Supabase Dashboard viser en advarsel, men mange ignorerer den.
2. For brede policies
-- FEIL: Alle kan lese alt
CREATE POLICY "bad_policy"
ON documents FOR SELECT
USING (true);
3. Ytelsesproblem med subqueries
Komplekse policies med mange JOINs kan bremse queries. Bruk indekser:
CREATE INDEX idx_team_members_user_id ON team_members (user_id);
CREATE INDEX idx_documents_team_id ON documents (team_id);
4. Glemmer service role
Supabase sin service_role-nøkkel bypasser RLS. Den skal aldri eksponeres til klienten. Bruk den kun i server-side kode (Next.js API Routes / Server Actions).
RLS i praksis: Norske SaaS-produkter
Flere norske produkter bygget med Supabase bruker RLS som grunnmur for sikkerhet:
- Boligposten — Eiendomsmeglere ser kun egne boliger og kampanjer. Team-basert tilgang lar meglerkontorer dele data internt.
- FamPlan — Familiemedlemmer ser kun sin egen families data. Barn har begrenset tilgang styrt av foreldrepolicies.
- Webagent.no — Multi-tenant SaaS der kunder kun ser egne prosjekter, fakturaer og AI-samtaler.
Alle disse bruker RLS kombinert med Next.js Server Components for å sikre at data aldri lekker — verken til andre brukere eller til klientsiden. For bedrifter som vurderer å bygge nettside med brukerdata, er RLS ikke valgfritt.
Oppsett i en Next.js-app
For en komplett gjennomgang av auth-oppsettet, se vår Supabase Auth guide.
Server Component (sikker)
// app/documents/page.tsx
import { createClient } from '@/lib/supabase/server';
export default async function DocumentsPage() {
const supabase = await createClient();
const { data: documents } = await supabase.from('documents').select('*');
// RLS filtrerer automatisk — kun brukerens dokumenter returneres
return (
<ul>
{documents?.map(doc => <li key={doc.id}>{doc.title}</li>)}
</ul>
);
}
Server Action (sikker)
'use server';
import { createClient } from '@/lib/supabase/server';
export async function createDocument(formData: FormData) {
const supabase = await createClient();
const { error } = await supabase.from('documents').insert({
title: formData.get('title'),
// owner_id settes IKKE her — RLS policy sikrer at auth.uid() brukes
});
if (error) throw new Error('Kunne ikke opprette dokument');
}
Sjekkliste for RLS
- [ ] RLS er aktivert på alle tabeller eksponert mot klienten
- [ ] SELECT, INSERT, UPDATE og DELETE policies er definert
- [ ]
service_role-nøkkel brukes kun server-side - [ ] Indekser er opprettet for kolonner brukt i policies
- [ ] Policies er testet med ulike brukerroller
- [ ] Ingen policy bruker
USING (true)uten god grunn
Oppsummering
Row Level Security er ikke valgfritt — det er obligatorisk for enhver Supabase-app som håndterer brukerdata. Det er enklere å sette opp enn du tror, og det gir en sikkerhet som er umulig å oppnå med applikasjonslogikk alene.
Start med de fire grunnleggende policies (SELECT, INSERT, UPDATE, DELETE basert på auth.uid()), og utvid derfra etter behov.
For AI-drevne applikasjoner med automatisering, er RLS spesielt viktig for å beskytte brukerdata.
Fra teori til praksis
Denne artikkelen er en del av hjelp.dev-eksperimentet — der vi tester om AI-drevet innhold kan bygge ekte autoritet. Alt innhold er åpent og GEO-optimalisert.
Bygget av Webagent AS i Bergen.