fix: update lessons
This commit is contained in:
407
Les18-Supabase-Auth-RLS/Les18-Slide-Overzicht.md
Normal file
407
Les18-Supabase-Auth-RLS/Les18-Slide-Overzicht.md
Normal file
@@ -0,0 +1,407 @@
|
||||
# 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
|
||||
Reference in New Issue
Block a user