408 lines
11 KiB
Markdown
408 lines
11 KiB
Markdown
# Les 18 — Supabase Auth + RLS
|
|
## Slide Overzicht (Klas A — 3 uur fysiek, demo-driven, **laatste les!**)
|
|
|
|
**Lesvorm:** Tim demonstreert klassikaal. Studenten kijken. Zelf bouwen = huiswerk.
|
|
**Demo-app:** Multi-user "Tasks" app met magic-link login + RLS
|
|
**Vervolg op:** Les 17 — Externe APIs in diepte
|
|
**Aansluit op:** Eindopdracht (thuis)
|
|
|
|
---
|
|
|
|
## Slide 1: Title
|
|
### Les 18 — Supabase Auth + RLS
|
|
|
|
**Visual:** "Les 18" BLUE, "Supabase Auth + RLS" BLACK, subtitle "Multi-user apps met per-user data isolation — productie ready"
|
|
|
|
---
|
|
|
|
## Slide 2: Terugblik + Curriculum recap
|
|
### Waar staan we?
|
|
|
|
**18 lessen — wat we hebben gedaan:**
|
|
- Les 1-10: Foundations (Next.js, Supabase basics, Tailwind, Shadcn, deploy basics, Auth intro)
|
|
- Les 11: AI SDK basics + Polderfest
|
|
- Les 12: Tool Calling
|
|
- Les 13: Agents
|
|
- Les 14: RAG + Embeddings
|
|
- Les 15: Cursor + Vercel deploy + GitHub Actions CI
|
|
- Les 16: MCP servers
|
|
- Les 17: Externe APIs in diepte
|
|
- **Les 18: Vandaag — Supabase Auth + RLS multi-user**
|
|
|
|
**Vandaag:** alles wat je hebt geleerd, multi-user maken. Klaar voor je eindopdracht.
|
|
|
|
---
|
|
|
|
## Slide 3: Planning
|
|
### Vandaag — 180 minuten
|
|
|
|
| Onderwerp | Duur |
|
|
|-----------|------|
|
|
| Terugblik + waarom auth | 10 min |
|
|
| Theorie: Supabase Auth basics | 20 min |
|
|
| Theorie: RLS — wat en waarom | 20 min |
|
|
| Theorie: Session in Next.js | 15 min |
|
|
| **Live Demo 1** — Auth setup + magic link | 25 min |
|
|
| **Live Demo 2** — Middleware + protected routes | 20 min |
|
|
| **Pauze** | 15 min |
|
|
| **Live Demo 3** — RLS policies multi-user | 30 min |
|
|
| **Live Demo 4** — Social login (GitHub) | 15 min |
|
|
| Eindopdracht recap | 5 min |
|
|
| Vragen + afsluiting cyclus | 15 min |
|
|
|
|
---
|
|
|
|
## Slide 4: Waarom auth + multi-user
|
|
### Echte apps hebben users
|
|
|
|
**Wat je niet kunt zonder auth:**
|
|
- Per-user data (mijn tasks, jouw tasks)
|
|
- Privacy (alleen jij ziet je data)
|
|
- Audit trail (wie deed wat wanneer)
|
|
- Subscriptions (Les 17 — wie betaalde, wie niet)
|
|
|
|
**Auth = 'wie ben jij?'**
|
|
**Authz = 'wat mag jij?'**
|
|
|
|
In Supabase: Auth + RLS doen beide. Geïntegreerd, simpel, productie-ready.
|
|
|
|
**Voor je eindopdracht:** als je app ÉÉN gebruiker heeft (bv. demo), kun je auth skippen. Maar als je app meerdere users heeft → must have.
|
|
|
|
---
|
|
|
|
## Slide 5: Supabase Auth basics
|
|
### Drie methoden
|
|
|
|
**1. Email + password (klassiek):**
|
|
- User registreert met email + wachtwoord
|
|
- Confirmation email
|
|
- Reset-flow voor vergeten wachtwoord
|
|
|
|
**2. Magic link (passwordless):**
|
|
- User vult email in → krijgt link in mail → klikt → ingelogd
|
|
- Geen wachtwoord te onthouden / lekken
|
|
- Modern, secure, simpel
|
|
|
|
**3. Social (OAuth):**
|
|
- "Login met Google", "Login met GitHub"
|
|
- User klikt → OAuth flow → Supabase regelt sessie
|
|
- Geen account-creation nodig
|
|
|
|
**Voor demo: magic link.** Snel, geen wachtwoord-hassle, productie-grade.
|
|
|
|
```typescript
|
|
await supabase.auth.signInWithOtp({
|
|
email: "user@example.com",
|
|
});
|
|
```
|
|
|
|
---
|
|
|
|
## Slide 6: RLS — wat en waarom
|
|
### Row Level Security
|
|
|
|
**Het probleem zonder RLS:**
|
|
|
|
```sql
|
|
-- Anyone with anon key can query everything
|
|
SELECT * FROM tasks;
|
|
-- Returns ALL tasks of ALL users — disaster
|
|
```
|
|
|
|
**Met RLS:**
|
|
|
|
```sql
|
|
-- Same query, maar Postgres filtert automatisch:
|
|
SELECT * FROM tasks WHERE user_id = auth.uid();
|
|
-- Returns alleen JOUW tasks
|
|
```
|
|
|
|
**RLS = filter dat Postgres ALTIJD toepast op queries**, ongeacht waar de query vandaan komt (client, server, anonymous, authenticated).
|
|
|
|
**Setup:**
|
|
|
|
```sql
|
|
alter table tasks enable row level security;
|
|
|
|
create policy "users see own tasks" on tasks
|
|
for select using (auth.uid() = user_id);
|
|
|
|
create policy "users insert own tasks" on tasks
|
|
for insert with check (auth.uid() = user_id);
|
|
```
|
|
|
|
**`auth.uid()`** is Supabase's helper — geeft de ingelogde user ID, of NULL als anonymous.
|
|
|
|
---
|
|
|
|
## Slide 7: Session in Next.js
|
|
### Cookies, middleware, server components
|
|
|
|
**Supabase Auth in Next.js (App Router):**
|
|
|
|
```bash
|
|
pnpm add @supabase/supabase-js @supabase/ssr
|
|
```
|
|
|
|
**Vier files:**
|
|
|
|
1. `utils/supabase/client.ts` — voor client components
|
|
2. `utils/supabase/server.ts` — voor server components + actions
|
|
3. `utils/supabase/middleware.ts` — refresh sessie elke request
|
|
4. `middleware.ts` (root) — gebruikt #3
|
|
|
|
**Waarom de complexiteit:**
|
|
- Cookies moeten gesynced tussen browser + server
|
|
- Sessies verlopen — middleware refreshed
|
|
- Voor server-rendering: auth state moet vooraf bekend zijn
|
|
|
|
Supabase heeft een **template** voor exact deze setup. Kopiëren werkt, snappen waarom is bonus.
|
|
|
|
---
|
|
|
|
## Slide 8: Wat we vandaag bouwen
|
|
### Multi-user Tasks app
|
|
|
|
**Doel:** simpele tasks-app, ieder z'n eigen lijst, geen vermenging.
|
|
|
|
**Tech:**
|
|
- Next.js 16
|
|
- Supabase Auth (magic link + GitHub OAuth)
|
|
- Supabase Postgres met RLS
|
|
- Tailwind + Shadcn
|
|
|
|
**Features:**
|
|
- `/login` — magic link form + GitHub button
|
|
- `/` — task list (alleen ingelogd)
|
|
- Add / complete / delete task — RLS bepaalt scope
|
|
- `/logout` — logout button
|
|
|
|
**Schema:**
|
|
```sql
|
|
create table tasks (
|
|
id bigserial primary key,
|
|
user_id uuid not null references auth.users(id),
|
|
text text not null,
|
|
done boolean default false,
|
|
created_at timestamp default now()
|
|
);
|
|
```
|
|
|
|
---
|
|
|
|
## Slide 9: LIVE DEMO 1 — Auth setup + magic link
|
|
### ~25 min
|
|
|
|
**Wat ik laat zien:**
|
|
1. `pnpm create next-app tasks-app + add @supabase/ssr @supabase/supabase-js`
|
|
2. Supabase project: Auth → Email → enable Magic Links
|
|
3. `utils/supabase/server.ts` + `client.ts` (template van Supabase docs)
|
|
4. `app/login/page.tsx`:
|
|
```tsx
|
|
"use client";
|
|
import { createClient } from "@/utils/supabase/client";
|
|
|
|
export default function Login() {
|
|
async function signIn(formData: FormData) {
|
|
const supabase = createClient();
|
|
await supabase.auth.signInWithOtp({
|
|
email: formData.get("email") as string,
|
|
options: { emailRedirectTo: `${location.origin}/auth/callback` },
|
|
});
|
|
alert("Check je email!");
|
|
}
|
|
return <form action={signIn}><input name="email" /><button>Login</button></form>;
|
|
}
|
|
```
|
|
5. `app/auth/callback/route.ts` — exchange code voor sessie
|
|
6. Test: email invullen → mail krijgen → klikken → ingelogd
|
|
|
|
---
|
|
|
|
## Slide 10: LIVE DEMO 2 — Middleware + protected routes
|
|
### ~20 min
|
|
|
|
**Wat ik laat zien:**
|
|
|
|
1. `middleware.ts` root:
|
|
```typescript
|
|
import { type NextRequest } from "next/server";
|
|
import { updateSession } from "@/utils/supabase/middleware";
|
|
|
|
export async function middleware(request: NextRequest) {
|
|
return await updateSession(request);
|
|
}
|
|
|
|
export const config = {
|
|
matcher: ["/((?!_next/static|_next/image|favicon.ico).*)"],
|
|
};
|
|
```
|
|
|
|
2. Server Component check:
|
|
```tsx
|
|
import { createClient } from "@/utils/supabase/server";
|
|
import { redirect } from "next/navigation";
|
|
|
|
export default async function Home() {
|
|
const supabase = await createClient();
|
|
const { data: { user } } = await supabase.auth.getUser();
|
|
if (!user) redirect("/login");
|
|
return <div>Welcome {user.email}</div>;
|
|
}
|
|
```
|
|
|
|
3. Test: niet ingelogd → redirect naar /login. Ingelogd → home page.
|
|
|
|
---
|
|
|
|
## Slide 11: Pauze
|
|
### 15 min
|
|
|
|
---
|
|
|
|
## Slide 12: LIVE DEMO 3 — RLS policies
|
|
### ~30 min
|
|
|
|
**Wat ik laat zien:**
|
|
|
|
1. `tasks` tabel + RLS aanzetten:
|
|
```sql
|
|
create table tasks (
|
|
id bigserial primary key,
|
|
user_id uuid not null references auth.users(id) default auth.uid(),
|
|
text text not null,
|
|
done boolean default false,
|
|
created_at timestamp default now()
|
|
);
|
|
|
|
alter table tasks enable row level security;
|
|
|
|
create policy "select own" on tasks
|
|
for select using (auth.uid() = user_id);
|
|
|
|
create policy "insert own" on tasks
|
|
for insert with check (auth.uid() = user_id);
|
|
|
|
create policy "update own" on tasks
|
|
for update using (auth.uid() = user_id);
|
|
|
|
create policy "delete own" on tasks
|
|
for delete using (auth.uid() = user_id);
|
|
```
|
|
|
|
2. Add task — werkt voor mij, niet voor ander account
|
|
3. Open tweede browser/incognito met ander account — andere data
|
|
4. **Demo: probeer SQL injection vanaf client** — RLS stopt het:
|
|
```typescript
|
|
// Probeer: "haal alle users hun tasks op"
|
|
const { data } = await supabase.from("tasks").select("*");
|
|
// Krijgt alleen JOUW tasks terug — RLS magic
|
|
```
|
|
|
|
---
|
|
|
|
## Slide 13: LIVE DEMO 4 — Social login (GitHub)
|
|
### ~15 min
|
|
|
|
**Wat ik laat zien:**
|
|
1. GitHub OAuth App aanmaken (Settings → Developer settings → OAuth Apps)
|
|
2. Callback URL: `https://your-project.supabase.co/auth/v1/callback`
|
|
3. Supabase Dashboard → Auth → Providers → GitHub → enable + client ID/secret
|
|
4. Login button:
|
|
```tsx
|
|
async function signInWithGitHub() {
|
|
const supabase = createClient();
|
|
await supabase.auth.signInWithOAuth({
|
|
provider: "github",
|
|
options: { redirectTo: `${location.origin}/auth/callback` },
|
|
});
|
|
}
|
|
```
|
|
5. Test: klik → GitHub → authorize → redirect terug → ingelogd
|
|
6. Database: zelfde RLS werkt — GitHub user heeft eigen `auth.uid()`
|
|
|
|
---
|
|
|
|
## Slide 14: Eindopdracht recap
|
|
### Hoe combineer je alles?
|
|
|
|
**Checklist voor je eindopdracht:**
|
|
|
|
- ✓ Next.js + TypeScript + Tailwind (Les 1-10)
|
|
- ✓ Supabase Postgres (Les 10)
|
|
- ✓ Vercel AI SDK met Tool Calling (Les 11-12)
|
|
- ✓ Agent met `stopWhen` (Les 13)
|
|
- ✓ RAG voor documenten (Les 14, optioneel)
|
|
- ✓ Cursor + GitHub + Vercel deploy (Les 15)
|
|
- ✓ Externe API (Les 17 patterns)
|
|
- ✓ Supabase Auth + RLS multi-user (Les 18)
|
|
|
|
**Ideaal eindopdracht-stack:**
|
|
- Multi-user app waarin elke user eigen data heeft
|
|
- AI feature met tool calling of agent
|
|
- Minstens 1 externe API integratie
|
|
- Deployed op Vercel met preview branches
|
|
- Code review via PR's
|
|
|
|
**Tijd:** ~40-60 uur thuis. Inleveren via Brightspace + repo URL + live URL.
|
|
|
|
---
|
|
|
|
## Slide 15: Afsluiting cyclus
|
|
### Bedankt en succes!
|
|
|
|
**Wat we samen gedaan hebben:**
|
|
- 18 lessen, ~54 uur klassikaal
|
|
- Van localhost naar productie
|
|
- Van simple fetch naar agents + RAG
|
|
- Van mock-data naar multi-user productie-apps
|
|
|
|
**Wat je nu zelfstandig kunt:**
|
|
- Een Next.js app van scratch naar productie
|
|
- AI SDK + tool calling + agents
|
|
- Externe APIs integreren met OAuth + webhooks
|
|
- Multi-user apps met auth + RLS
|
|
- Code reviews + CI/CD pipelines
|
|
|
|
**Voor je eindopdracht:**
|
|
- Werk er gestaag aan (niet laatste week 80 uur)
|
|
- Push klein en vaak
|
|
- Vraag hulp — Slack, mail, office hours
|
|
- Eindopdracht-presentatie volgt na inleveren
|
|
|
|
**Bedankt voor jullie inzet. Veel succes met de eindopdracht.**
|
|
|
|
---
|
|
|
|
## Slide Summary
|
|
|
|
| # | Title | Type |
|
|
|---|-------|------|
|
|
| 1 | Title | Opening |
|
|
| 2 | Terugblik 18 lessen | Recap |
|
|
| 3 | Planning | 180-min |
|
|
| 4 | Waarom auth + multi-user | Theorie |
|
|
| 5 | Supabase Auth basics | Theorie |
|
|
| 6 | RLS — wat en waarom | Theorie |
|
|
| 7 | Session in Next.js | Theorie |
|
|
| 8 | Wat we bouwen | Intro |
|
|
| 9 | **DEMO 1** — Auth + magic link | Demo |
|
|
| 10 | **DEMO 2** — Middleware + protected | Demo |
|
|
| 11 | Pauze | Break |
|
|
| 12 | **DEMO 3** — RLS policies | Demo |
|
|
| 13 | **DEMO 4** — Social login | Demo |
|
|
| 14 | Eindopdracht checklist | Praktijk |
|
|
| 15 | Afsluiting cyclus | Closing |
|
|
|
|
---
|
|
|
|
## Bronnen
|
|
|
|
- **Supabase Auth docs:** https://supabase.com/docs/guides/auth
|
|
- **Supabase Next.js setup:** https://supabase.com/docs/guides/auth/server-side/nextjs
|
|
- **Supabase Auth UI:** https://supabase.com/docs/guides/auth/auth-helpers/auth-ui
|
|
- **RLS guide:** https://supabase.com/docs/guides/database/postgres/row-level-security
|
|
- **RLS policies in praktijk:** https://supabase.com/docs/guides/database/postgres/row-level-security#policies
|
|
- **Magic links:** https://supabase.com/docs/guides/auth/auth-magic-link
|
|
- **OAuth providers:** https://supabase.com/docs/guides/auth/social-login
|