# Les 10 -- Slide-overzicht
## Supabase Authenticatie & Row Level Security (15 slides)

**Cursus:** AI Developer -- NOVI Hogeschool Utrecht
**Duur:** 3 uur (180 minuten) | 09:00 - 12:00
**Project:** Poll App -- authenticatie toevoegen met Supabase Auth

> **Voorkennis studenten:** Supabase basics uit Les 8 (project setup, database, tabellen). Poll App met Next.js 16, TypeScript, Tailwind CSS. Database met `polls` en `options` tabellen. Nog geen authenticatie.

---

## Timing-overzicht

| Tijd          | Duur   | Onderwerp                              | Slide(s) | Vorm               |
|---------------|--------|----------------------------------------|----------|---------------------|
| 09:00 - 09:05 | 5 min  | Titelslide                             | 1        | Presentatie         |
| 09:05 - 09:10 | 5 min  | Planning vandaag                       | 2        | Presentatie         |
| 09:10 - 09:15 | 5 min  | Terugblik Les 8-9                      | 3        | Presentatie         |
| 09:15 - 09:25 | 10 min | Eindexamenopdracht                     | 4        | Presentatie         |
| 09:25 - 09:30 | 5 min  | Wat is authenticatie?                  | 5        | Presentatie         |
| 09:30 - 09:40 | 10 min | Supabase Auth -- 3 methodes            | 6        | Presentatie + Demo  |
| 09:40 - 09:45 | 5 min  | Hoe werkt een sessie?                  | 7        | Presentatie         |
| 09:45 - 09:55 | 10 min | Auth in Next.js                        | 8        | Presentatie         |
| 09:55 - 10:00 | 5 min  | Row Level Security (RLS)               | 9        | Presentatie         |
| 10:00 - 10:15 | 15 min | Pauze                                  | 10       | Pauze               |
| 10:15 - 10:30 | 15 min | Hands-on: Auth opzetten in Supabase    | 11       | Hands-on (ref)      |
| 10:30 - 10:55 | 25 min | Hands-on: Login & registratie          | 12       | Hands-on (ref)      |
| 10:55 - 11:10 | 15 min | Hands-on: Sessie & beschermde routes   | 13       | Hands-on (ref)      |
| 11:10 - 11:25 | 15 min | Hands-on: Basis RLS                    | 14       | Hands-on (ref)      |
| 11:45 - 12:00 | 15 min | Samenvatting & huiswerk                | 15       | Presentatie         |

---

## Slide-indeling

---

### Slide 1: Titelslide
**Timing:** 09:00 - 09:05 (5 min)

**Titel:** Les 10 -- Supabase Auth & RLS
**Ondertitel:** Authenticatie toevoegen aan de Poll App

```
+========================================================+
|                                                        |
|            LES 10: SUPABASE AUTH & RLS                 |
|                                                        |
|   Authenticatie toevoegen aan de Poll App              |
|                                                        |
|   Tim -- NOVI Hogeschool Utrecht                       |
|   AI Developer Cursus                                  |
|                                                        |
+========================================================+
```

**Kernpunten:**
- Les 10 van de AI Developer cursus
- Vandaag draaien we alles om beveiliging: wie mag wat?
- Supabase Auth + Row Level Security
- We bouwen verder op de Poll App uit Les 8-9

**Spreektekst:**
- "Goedemorgen allemaal, welkom bij Les 10!"
- "Vandaag gaan we onze Poll App beveiligen met echte authenticatie."
- "Na vandaag kan niet zomaar iedereen meer polls aanmaken -- je moet ingelogd zijn."
- "We gebruiken Supabase Auth, wat het hele inlogproces voor ons afhandelt."
- "En we leren over Row Level Security: beveiliging op database-niveau."

---

### Slide 2: Planning Vandaag
**Timing:** 09:05 - 09:10 (5 min)

**Titel:** Planning Vandaag
**Ondertitel:** Wat gaan we doen en leren?

```
+========================================================+
|  PLANNING VANDAAG                                      |
|                                                        |
|  09:00  Theorie: Auth & RLS concepten                  |
|  09:15  Eindexamenopdracht introductie                 |
|  09:25  Supabase Auth deep dive                        |
|  10:00  -- PAUZE --                                    |
|  10:15  Hands-on: Auth opzetten                        |
|  10:30  Hands-on: Login & registratie                  |
|  10:55  Hands-on: Sessie & beschermde routes           |
|  11:10  Hands-on: Basis RLS                            |
|  11:45  Samenvatting & huiswerk                        |
|                                                        |
|  LEERDOELEN:                                           |
|  [x] Supabase Auth instellen (email/password)          |
|  [x] Login/registratie pagina bouwen                   |
|  [x] Sessie beheren (wie is ingelogd?)                 |
|  [x] Row Level Security policies schrijven             |
+========================================================+
```

**Kernpunten:**
- Eerste helft: theorie over authenticatie, autorisatie, sessies
- Introductie van de eindexamenopdracht
- Tweede helft: volledig hands-on, stap voor stap
- Vier concrete leerdoelen die je vandaag behaalt

**Spreektekst:**
- "Dit is de planning voor vandaag. We beginnen met een stuk theorie."
- "Daarna introduceer ik de eindexamenopdracht -- heel belangrijk."
- "Na de pauze gaan we volledig hands-on. Ik laat reference slides zien die op het scherm blijven staan terwijl jullie werken."
- "Aan het einde van vandaag heb je een werkende login, registratie, en beveiligde database."
- "Vier leerdoelen: Auth instellen, login bouwen, sessie beheren, en RLS schrijven."

---

### Slide 3: Terugblik Les 8-9
**Timing:** 09:10 - 09:15 (5 min)

**Titel:** Terugblik Les 8-9
**Ondertitel:** Waar staan we nu?

```
+========================================================+
|  TERUGBLIK LES 8-9                                     |
|                                                        |
|  +------------------+    +-------------------------+   |
|  |  SUPABASE        |    |  NEXT.JS APP            |   |
|  |                  |    |                         |   |
|  |  polls           |    |  / (homepage)           |   |
|  |  +----------+    |    |  /polls (lijst)         |   |
|  |  | id       |    |<-->|  /polls/new (aanmaken)  |   |
|  |  | question |    |    |  /polls/[id] (stemmen)  |   |
|  |  +----------+    |    |                         |   |
|  |                  |    |                         |   |
|  |  options         |    |  TypeScript + Tailwind  |   |
|  |  +----------+    |    |                         |   |
|  |  | id       |    |    +-------------------------+   |
|  |  | poll_id  |    |                                  |
|  |  | text     |    |    PROBLEEM:                     |
|  |  | votes    |    |    Iedereen kan alles!           |
|  |  +----------+    |    Geen login nodig              |
|  +------------------+    Geen beveiliging              |
+========================================================+
```

**Kernpunten:**
- Supabase project met `polls` en `options` tabellen
- Next.js app met pagina's voor lijst, aanmaken en stemmen
- Alles werkt, maar er is geen beveiliging
- Het probleem: iedereen kan polls aanmaken en data manipuleren

**Spreektekst:**
- "Laten we even terugkijken naar waar we staan."
- "We hebben een werkende Poll App: je kunt polls bekijken, nieuwe polls aanmaken, en stemmen."
- "De database draait op Supabase met twee tabellen: polls en options."
- "Maar... er is een groot probleem. Iedereen kan alles doen. Er is geen login."
- "Als je de Supabase URL en API key kent, kun je direct de database benaderen."
- "Vandaag gaan we dat oplossen!"

---

### Slide 4: Eindexamenopdracht
**Timing:** 09:15 - 09:25 (10 min)

**Titel:** Eindexamenopdracht
**Ondertitel:** Vrije keuze app -- jouw project!

```
+========================================================+
|  EINDEXAMENOPDRACHT                                    |
|                                                        |
|  Bouw je eigen full-stack applicatie!                   |
|                                                        |
|  VEREISTEN:                                            |
|  +----------------------------------------------------+|
|  | [x] Next.js 16 + TypeScript                        ||
|  | [x] Supabase (database + auth)                     ||
|  | [x] Authenticatie (login/registratie)               ||
|  | [x] Row Level Security (RLS policies)              ||
|  | [x] CRUD operaties (Create, Read, Update, Delete)  ||
|  | [x] Deployed (Vercel + Supabase)                   ||
|  | [x] Nette code (componenten, types, error handling)||
|  +----------------------------------------------------+|
|                                                        |
|  TIJDLIJN:                                             |
|  Les 10 (vandaag)   Introductie + Auth leren           |
|  Les 11-12          Bouwen aan je project              |
|  Les 13             Inleveren + presentatie            |
|                                                        |
|  IDEEN: Todo app, Blog, Recepten, Budget tracker,     |
|         Quiz app, Bookmark manager, Habit tracker...   |
+========================================================+
```

**Kernpunten:**
- Vrije keuze: kies zelf welke app je bouwt
- Zeven technische vereisten waar je aan moet voldoen
- Alles wat we in de cursus geleerd hebben komt samen
- Tijdlijn: 3-4 lessen om te bouwen, daarna inleveren + presentatie
- Voorbeelden ter inspiratie, maar eigen ideeen zijn welkom

**Spreektekst:**
- "Nu iets heel belangrijks: de eindexamenopdracht."
- "Jullie gaan je eigen full-stack applicatie bouwen. Vrije keuze -- dus kies iets dat je leuk vindt."
- "Er zijn zeven vereisten. Laten we ze doorlopen."
- "Next.js 16 met TypeScript -- dat kennen jullie al."
- "Supabase voor de database EN authenticatie -- dat leren we vandaag."
- "Je app moet login en registratie hebben, en de database moet beveiligd zijn met RLS."
- "CRUD: je moet data kunnen aanmaken, lezen, updaten en verwijderen."
- "De app moet gedeployed zijn op Vercel, zodat ik hem kan bekijken."
- "En nette code: goede componenten, TypeScript types, error handling."
- "Qua tijdlijn: vandaag leer je Auth. De komende lessen heb je tijd om te bouwen. En dan presenteren."
- "Begin alvast na te denken over wat je wilt bouwen. Een todo app, blog, recepten-app, budget tracker... het mag allemaal."

---

### Slide 5: Wat is Authenticatie?
**Timing:** 09:25 - 09:30 (5 min)

**Titel:** Wat is Authenticatie?
**Ondertitel:** Auth vs Autorisatie

```
+========================================================+
|  WAT IS AUTHENTICATIE?                                 |
|                                                        |
|  +------------------------+  +------------------------+|
|  |                        |  |                        ||
|  |      AUTHENTICATIE     |  |      AUTORISATIE       ||
|  |                        |  |                        ||
|  |    "Wie ben je?"       |  |    "Wat mag je?"       ||
|  |                        |  |                        ||
|  |    +--------+          |  |    +--------+          ||
|  |    | SLOT   |          |  |    | SCHILD |          ||
|  |    | [====] |          |  |    |  {X}   |          ||
|  |    +--------+          |  |    +--------+          ||
|  |                        |  |                        ||
|  |  - Inloggen            |  |  - Rechten             ||
|  |  - Email + wachtwoord  |  |  - Rollen (admin/user) ||
|  |  - Identiteit bewijzen |  |  - Wat mag je zien?    ||
|  |                        |  |                        ||
|  +------------------------+  +------------------------+|
|                                                        |
|  VOORBEELD:                                            |
|  Bioscoop: ticket tonen = auth | stoel kiezen = autor. |
|  School:   pasje scannen = auth | lokaal betreden = a. |
+========================================================+
```

**Kernpunten:**
- Authenticatie = wie ben je? Identiteit bewijzen.
- Autorisatie = wat mag je? Rechten en rollen.
- Twee aparte concepten die samenwerken
- Vandaag leren we beide: Auth met Supabase, Autorisatie met RLS

**Spreektekst:**
- "Voordat we gaan bouwen, moeten we twee begrippen begrijpen."
- "Authenticatie: wie ben je? Je bewijst je identiteit. Bijvoorbeeld door in te loggen met email en wachtwoord."
- "Autorisatie: wat mag je? Welke rechten heb je? Mag je alleen lezen, of ook schrijven?"
- "Denk aan een bioscoop. Je laat je ticket zien bij de ingang -- dat is authenticatie. Maar je mag alleen in zaal 3 zitten -- dat is autorisatie."
- "Of school: je scant je pasje -- authenticatie. Maar je mag niet zomaar in elk lokaal -- autorisatie."
- "Vandaag leren we beide. Supabase Auth regelt de authenticatie. Row Level Security regelt de autorisatie."

---

### Slide 6: Supabase Auth -- 3 Methodes
**Timing:** 09:30 - 09:40 (10 min)

**Titel:** Supabase Auth -- 3 Methodes
**Ondertitel:** Hoe kunnen gebruikers inloggen?

```
+========================================================+
|  SUPABASE AUTH -- 3 METHODES                           |
|                                                        |
|  +----------------+ +----------------+ +--------------+|
|  | EMAIL/PASSWORD | | MAGIC LINK     | | GOOGLE OAUTH ||
|  |                | |                | |              ||
|  | +------------+ | | +------------+ | | +----------+||
|  | | email:     | | | | email:     | | | |  Google  |||
|  | | [........] | | | | [........] | | | |  [G]     |||
|  | | password:  | | | |            | | | |  Login   |||
|  | | [........] | | | | Klik link  | | | +----------+||
|  | | [INLOGGEN] | | | | in je mail | | |            ||
|  | +------------+ | | +------------+ | |              ||
|  |                | |                | |              ||
|  | Klassiek       | | Geen wachtw.  | | Social login ||
|  | Makkelijk te   | | Veilig        | | Makkelijkst  ||
|  | begrijpen      | | Simpel        | | voor users   ||
|  |                | |                | |              ||
|  | WIJ GEBRUIKEN  | | OPTIONEEL     | | NIET VANDAAG ||
|  | DIT VANDAAG    | | (bonus)       | | (complex)    ||
|  +----------------+ +----------------+ +--------------+|
+========================================================+
```

**Kernpunten:**
- Email/Password: klassieke methode, makkelijk te begrijpen, we gebruiken dit vandaag
- Magic Link: email zonder wachtwoord, gebruiker klikt link in mailbox
- Google OAuth: social login via Google account, meest gebruiksvriendelijk maar complexer
- Supabase ondersteunt alle drie out-of-the-box
- In het Supabase Dashboard kun je providers aan/uitzetten

**Spreektekst:**
- "Supabase biedt drie manieren om in te loggen. Laten we ze bekijken."
- "Nummer 1: email en wachtwoord. De klassieke manier. Je maakt een account aan met je email en een wachtwoord, en daarna log je in. Dit gaan we vandaag gebruiken."
- "Nummer 2: Magic Link. Je vult alleen je email in, en Supabase stuurt een linkje. Klik erop en je bent ingelogd. Geen wachtwoord nodig. Dit voegen we toe als bonus."
- "Nummer 3: Google OAuth. De 'Log in met Google' knop die je overal ziet. Heel handig voor gebruikers, maar de setup is complexer. Dat doen we niet vandaag."
- "Laat me even het Supabase Dashboard laten zien waar je deze providers configureert..."
- *Tim opent Supabase Dashboard > Authentication > Providers en laat de opties zien*
- "Zie je? Email staat standaard aan. Je kunt hier ook Magic Link, Google, GitHub en meer aanzetten."

---

### Slide 7: Hoe Werkt een Sessie?
**Timing:** 09:40 - 09:45 (5 min)

**Titel:** Hoe Werkt een Sessie?
**Ondertitel:** Van login tot beveiligde requests

```
+========================================================+
|  HOE WERKT EEN SESSIE?                                 |
|                                                        |
|  +--------+      +----------+      +-----------+       |
|  | USER   |----->| SUPABASE |----->| JWT TOKEN |       |
|  | login  |      | Auth     |      | (sessie)  |       |
|  +--------+      +----------+      +-----------+       |
|      |                                   |              |
|      |   email + wachtwoord              |              |
|      |                                   v              |
|      |                          +---------------+       |
|      |                          | COOKIE        |       |
|      |                          | (browser)     |       |
|      |                          +---------------+       |
|      |                                   |              |
|      v                                   v              |
|  +---------------------------------------------+       |
|  |  ELKE REQUEST                                |       |
|  |  Browser stuurt cookie automatisch mee       |       |
|  |  Middleware checkt: is de sessie geldig?      |       |
|  |  Zo ja: door naar de pagina                  |       |
|  |  Zo nee: redirect naar /login                |       |
|  +---------------------------------------------+       |
+========================================================+
```

**Kernpunten:**
- User logt in met email + wachtwoord
- Supabase Auth verifieert en stuurt een JWT token terug
- JWT wordt opgeslagen als cookie in de browser
- Bij elke request stuurt de browser de cookie automatisch mee
- Middleware controleert of de sessie geldig is

**Spreektekst:**
- "Hoe werkt een sessie eigenlijk? Laten we het stap voor stap bekijken."
- "Stap 1: de gebruiker logt in met email en wachtwoord."
- "Stap 2: Supabase Auth controleert de gegevens. Kloppen ze? Dan krijg je een JWT token terug."
- "JWT staat voor JSON Web Token. Het is een gecodeerde string die zegt: 'deze gebruiker is ingelogd en dit is hun user ID'."
- "Stap 3: dat token wordt opgeslagen als cookie in de browser."
- "Stap 4: bij elke pagina die je bezoekt, stuurt de browser die cookie automatisch mee."
- "In onze Next.js app hebben we middleware die bij elke request checkt: is er een geldige sessie? Zo nee, dan redirect hij naar /login."
- "Dit is belangrijk om te begrijpen. Je hoeft het niet zelf te bouwen -- Supabase en de @supabase/ssr package regelen het voor je."

---

### Slide 8: Auth in Next.js
**Timing:** 09:45 - 09:55 (10 min)

**Titel:** Auth in Next.js
**Ondertitel:** Architectuur en bestandsstructuur

```
+========================================================+
|  AUTH IN NEXT.JS -- ARCHITECTUUR                       |
|                                                        |
|  Package: @supabase/ssr                                |
|                                                        |
|  +----------------------------------------------------+|
|  |  BESTANDEN DIE WE GAAN MAKEN:                      ||
|  |                                                    ||
|  |  src/                                              ||
|  |  +-- lib/supabase/                                 ||
|  |  |   +-- client.ts     <-- Browser client          ||
|  |  |   +-- server.ts     <-- Server client           ||
|  |  |                                                 ||
|  |  +-- middleware.ts      <-- Sessie refreshen        ||
|  |  |                                                 ||
|  |  +-- app/                                          ||
|  |      +-- auth/                                     ||
|  |      |   +-- callback/                             ||
|  |      |       +-- route.ts  <-- Auth callback       ||
|  |      +-- login/                                    ||
|  |          +-- page.tsx   <-- Login pagina            ||
|  +----------------------------------------------------+|
|                                                        |
|  BROWSER CLIENT         SERVER CLIENT                  |
|  - Gebruikt in          - Gebruikt in Server           |
|    Client Components      Components & API routes      |
|  - createBrowser...     - createServer...              |
|  - Leest cookies        - Leest EN schrijft cookies    |
+========================================================+
```

**Kernpunten:**
- `@supabase/ssr` is de package die cookies en sessies afhandelt
- Browser client: voor Client Components (interactieve UI)
- Server client: voor Server Components en API routes (data ophalen)
- Middleware: draait bij elke request, refresht de sessie
- Auth callback route: verwerkt magic links en OAuth redirects

**Spreektekst:**
- "Laten we kijken naar de architectuur. We installeren het package @supabase/ssr."
- "We maken twee Supabase clients. Waarom twee? Omdat Next.js twee omgevingen heeft."
- "De browser client gebruik je in Client Components -- dat zijn de interactieve componenten met useState en onClick."
- "De server client gebruik je in Server Components en API routes -- dat is code die op de server draait."
- "Het verschil: de browser client kan alleen cookies lezen. De server client kan ze ook schrijven."
- "Dan hebben we middleware.ts. Die draait bij ELKE request. Zijn taak: de sessie refreshen zodat de gebruiker ingelogd blijft."
- "En de auth callback route. Die is nodig voor magic links. Als een gebruiker op een magic link klikt, komt hij terug op /auth/callback, en die route wisselt de code om voor een sessie."
- "Na de pauze gaan we al deze bestanden stap voor stap aanmaken."

---

### Slide 9: Row Level Security (RLS)
**Timing:** 09:55 - 10:00 (5 min)

**Titel:** Row Level Security (RLS)
**Ondertitel:** Beveiliging op database-niveau

```
+========================================================+
|  ROW LEVEL SECURITY (RLS)                              |
|                                                        |
|  ZONDER RLS:                                           |
|  +--------------------------------------------+        |
|  | polls tabel                    GEEN SLOT    |        |
|  | id | question       | created_by            |        |
|  | 1  | Beste taal?    | user_abc   <- leesbaar|        |
|  | 2  | Beste editor?  | user_xyz   <- leesbaar|        |
|  | ** Iedereen kan ALLES lezen, schrijven,     |        |
|  |    updaten en verwijderen **                |        |
|  +--------------------------------------------+        |
|                                                        |
|  MET RLS:                                              |
|  +--------------------------------------------+        |
|  | polls tabel                    [SLOT]       |        |
|  | id | question       | created_by            |        |
|  | 1  | Beste taal?    | user_abc   <- policy! |        |
|  | 2  | Beste editor?  | user_xyz   <- policy! |        |
|  |                                             |        |
|  | POLICIES:                                   |        |
|  | SELECT: iedereen mag lezen                  |        |
|  | INSERT: alleen ingelogde users              |        |
|  | UPDATE: alleen eigen polls                  |        |
|  | DELETE: alleen eigen polls                  |        |
|  +--------------------------------------------+        |
+========================================================+
```

**Kernpunten:**
- Zonder RLS: de API key geeft volledige toegang tot alle data
- Met RLS: elke query wordt gecheckt tegen policies
- Policies definieer je per tabel, per operatie (SELECT, INSERT, UPDATE, DELETE)
- RLS draait op database-niveau -- je kunt het niet omzeilen vanuit de frontend
- Dit is de autorisatie-laag die we eerder bespraken

**Spreektekst:**
- "Nu het laatste theoretische concept: Row Level Security, oftewel RLS."
- "Onthoud: onze Supabase API key staat in de frontend code. Iedereen kan die zien."
- "Zonder RLS kan iemand met die key ALLES doen: lezen, schrijven, verwijderen. Dat is een groot beveiligingsprobleem."
- "Met RLS zet je een slot op je tabellen. Je definieert policies: regels die zeggen wie wat mag."
- "Bijvoorbeeld: iedereen mag polls LEZEN, maar alleen ingelogde users mogen polls AANMAKEN."
- "Of: je mag alleen je EIGEN polls updaten of verwijderen."
- "Het mooie van RLS is dat het op database-niveau draait. Zelfs als iemand direct de API aanroept, worden de policies gecontroleerd."
- "Na de pauze gaan we dit ook daadwerkelijk instellen."

---

### Slide 10: Pauze
**Timing:** 10:00 - 10:15 (15 min)

**Titel:** Pauze
**Ondertitel:** 15 minuten

```
+========================================================+
|                                                        |
|                                                        |
|                      PAUZE                             |
|                                                        |
|                   15 minuten                           |
|                                                        |
|              10:00 -- 10:15                             |
|                                                        |
|                                                        |
|   Tip: Denk alvast na over je eindexamenopdracht!      |
|   Welke app wil je bouwen?                             |
|                                                        |
|                                                        |
+========================================================+
```

**Kernpunten:**
- 15 minuten pauze
- Studenten kunnen alvast nadenken over hun eindexamenopdracht
- Na de pauze: hands-on, dus zorg dat je laptop klaar staat

**Spreektekst:**
- "Oké, tijd voor pauze! 15 minuten."
- "Denk alvast na over wat je wilt bouwen voor je eindexamenopdracht."
- "Na de pauze gaan we direct aan de slag. Zorg dat je Cursor open hebt en je Supabase project klaarstaat."
- "Tot zo!"

---

### Slide 11: Hands-on -- Auth Opzetten in Supabase (REFERENCE SLIDE)
**Timing:** 10:15 - 10:30 (15 min)

**Titel:** Hands-on -- Auth Opzetten
**Ondertitel:** REFERENCE SLIDE -- blijft op het scherm

```
+========================================================+
|  HANDS-ON: AUTH OPZETTEN                    [REF]      |
|                                                        |
|  STAP 1: Starter project                               |
|  $ Pak de starter zip uit en open in Cursor            |
|  $ npm install                                         |
|                                                        |
|  STAP 2: Environment variabelen                        |
|  Maak .env.local:                                      |
|  NEXT_PUBLIC_SUPABASE_URL=https://xxx.supabase.co      |
|  NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGci...             |
|                                                        |
|  STAP 3: Packages installeren                          |
|  $ npm install @supabase/ssr @supabase/supabase-js     |
|                                                        |
|  STAP 4: Browser client (src/lib/supabase/client.ts)   |
|  import { createBrowserClient } from '@supabase/ssr'   |
|  export function createClient() {                      |
|    return createBrowserClient(                         |
|      process.env.NEXT_PUBLIC_SUPABASE_URL!,            |
|      process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!        |
|    )                                                   |
|  }                                                     |
|                                                        |
|  STAP 5: Server client (src/lib/supabase/server.ts)    |
|  import { createServerClient } from '@supabase/ssr'    |
|  import { cookies } from 'next/headers'                |
|  export async function createClient() {                |
|    const cookieStore = await cookies()                 |
|    return createServerClient(                          |
|      process.env.NEXT_PUBLIC_SUPABASE_URL!,            |
|      process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,       |
|      { cookies: {                                      |
|          getAll() { return cookieStore.getAll() },     |
|          setAll(cookiesToSet) {                         |
|            cookiesToSet.forEach(({ name, value, opts })|
|              => cookieStore.set(name, value, opts))    |
|          },                                            |
|      }}                                                |
|    )                                                   |
|  }                                                     |
|                                                        |
|  STAP 6: Middleware (src/middleware.ts)                 |
|  import { createServerClient } from '@supabase/ssr'    |
|  import { NextResponse, type NextRequest } from 'next' |
|  export async function middleware(request: NextRequest) |
|    // Refresh sessie bij elke request                  |
|    // Redirect naar /login als niet ingelogd           |
|                                                        |
|  STAP 7: Auth callback (src/app/auth/callback/route.ts)|
|  // Verwerkt magic link codes                          |
|  // Wisselt code om voor sessie                        |
|                                                        |
|  STAP 8: Supabase Dashboard                            |
|  Authentication > Providers > Email aanvinken          |
+========================================================+
```

**Kernpunten:**
- Stap 1-2: Project openen en environment variabelen instellen
- Stap 3: Twee packages installeren: `@supabase/ssr` en `@supabase/supabase-js`
- Stap 4: Browser client voor Client Components
- Stap 5: Server client met cookie-handling voor Server Components
- Stap 6: Middleware die sessie refresht en ongeautoriseerde users redirect
- Stap 7: Auth callback route voor magic links
- Stap 8: Email provider aanzetten in Supabase Dashboard

**Spreektekst:**
- "Oké, we gaan aan de slag! Deze slide blijft op het scherm. Volg de stappen."
- "Stap 1: pak de starter zip uit die ik gestuurd heb, en open het project in Cursor. Doe npm install."
- "Stap 2: maak een .env.local bestand. Kopieer je Supabase URL en anon key uit het dashboard."
- "Stap 3: installeer de twee packages: @supabase/ssr en @supabase/supabase-js."
- "Stap 4: maak de browser client aan. Dit is heel simpel -- je roept createBrowserClient aan met je URL en key."
- "Stap 5: de server client is iets complexer. Die heeft cookie-handling nodig. Kopieer de code van de slide."
- "Stap 6: de middleware. Die zorgt dat de sessie bij elke request ververst wordt. En hij redirect naar /login als je niet ingelogd bent."
- "Stap 7: de auth callback route. Die heb je nodig voor magic links."
- "Stap 8: ga naar je Supabase Dashboard, Authentication, Providers, en zet Email aan."
- "Neem de tijd, volg het stap voor stap. Ik loop rond om te helpen."

---

### Slide 12: Hands-on -- Login & Registratie (REFERENCE SLIDE)
**Timing:** 10:30 - 10:55 (25 min)

**Titel:** Hands-on -- Login & Registratie
**Ondertitel:** REFERENCE SLIDE -- blijft op het scherm

```
+========================================================+
|  HANDS-ON: LOGIN & REGISTRATIE              [REF]      |
|                                                        |
|  STAP 1: Maak src/app/login/page.tsx                   |
|  'use client'                                          |
|  import { useState } from 'react'                      |
|  import { createClient } from '@/lib/supabase/client'  |
|  import { useRouter } from 'next/navigation'           |
|                                                        |
|  STAP 2: State                                         |
|  const [email, setEmail] = useState('')                |
|  const [password, setPassword] = useState('')          |
|  const [loading, setLoading] = useState(false)         |
|  const [message, setMessage] = useState('')            |
|  const router = useRouter()                            |
|  const supabase = createClient()                       |
|                                                        |
|  STAP 3: handleSignUp functie                          |
|  async function handleSignUp() {                       |
|    setLoading(true)                                    |
|    const { error } = await supabase.auth.signUp({      |
|      email, password                                   |
|    })                                                  |
|    if (error) setMessage(error.message)                |
|    else setMessage('Check je email voor confirmatie!') |
|    setLoading(false)                                   |
|  }                                                     |
|                                                        |
|  STAP 4: handleSignIn functie                          |
|  async function handleSignIn() {                       |
|    setLoading(true)                                    |
|    const { error } = await                             |
|      supabase.auth.signInWithPassword({                |
|        email, password                                 |
|      })                                                |
|    if (error) setMessage(error.message)                |
|    else router.push('/')                               |
|    setLoading(false)                                   |
|  }                                                     |
|                                                        |
|  STAP 5: handleMagicLink functie (bonus)               |
|  async function handleMagicLink() {                    |
|    setLoading(true)                                    |
|    const { error } = await                             |
|      supabase.auth.signInWithOtp({ email })            |
|    if (error) setMessage(error.message)                |
|    else setMessage('Check je email voor de link!')     |
|    setLoading(false)                                   |
|  }                                                     |
|                                                        |
|  STAP 6: Formulier JSX                                 |
|  <form>                                                |
|    <input type="email" value={email}                   |
|           onChange={e => setEmail(e.target.value)} />   |
|    <input type="password" value={password}             |
|           onChange={e => setPassword(e.target.value)} />|
|    <button onClick={handleSignIn}>Inloggen</button>    |
|    <button onClick={handleSignUp}>Registreren</button> |
|    <button onClick={handleMagicLink}>Magic Link</but.> |
|    {message && <p>{message}</p>}                       |
|  </form>                                               |
|                                                        |
|  STAP 7: Error handling & loading states               |
|  - disabled={loading} op alle buttons                  |
|  - Loading tekst: "Even geduld..."                     |
|  - Error message tonen in rode tekst                   |
|                                                        |
|  STAP 8: Testen                                        |
|  - Registreer een account (check Supabase Dashboard)   |
|  - Log in met dat account                              |
|  - Probeer magic link (check je email)                 |
+========================================================+
```

**Kernpunten:**
- Login pagina als Client Component (`'use client'`)
- State voor email, password, loading en foutmeldingen
- `signUp()`: registratie, stuurt een bevestigingsmail
- `signInWithPassword()`: inloggen met email + wachtwoord
- `signInWithOtp()`: magic link versturen (bonus)
- Formulier met drie buttons: inloggen, registreren, magic link
- Na succesvolle login: redirect naar homepage
- Testen: registreer, log in, check Supabase Dashboard

**Spreektekst:**
- "Nu gaan we de login pagina bouwen. Dit wordt een Client Component omdat we interactie nodig hebben."
- "Stap 1: maak het bestand src/app/login/page.tsx aan."
- "Stap 2: we hebben vier stukken state nodig: email, password, loading, en een message voor foutmeldingen."
- "Stap 3: de handleSignUp functie. Die roept supabase.auth.signUp aan met de email en het wachtwoord. Als het lukt, krijgt de gebruiker een bevestigingsmail."
- "Stap 4: handleSignIn. Dit is het echte inloggen. signInWithPassword. Als het lukt, redirect je naar de homepage."
- "Stap 5: de magic link als bonus. signInWithOtp -- je geeft alleen de email mee, geen wachtwoord."
- "Stap 6: het formulier. Twee input velden en drie buttons. Simpel."
- "Stap 7: vergeet de error handling niet! Disable de buttons als het laden is, en toon foutmeldingen."
- "Stap 8: testen! Registreer een account en check in het Supabase Dashboard of de user verschijnt onder Authentication > Users."
- "Dit is de meest uitgebreide stap, dus neem je tijd. Ik loop rond."

---

### Slide 13: Hands-on -- Sessie & Beschermde Routes (REFERENCE SLIDE)
**Timing:** 10:55 - 11:10 (15 min)

**Titel:** Hands-on -- Sessie & Beschermde Routes
**Ondertitel:** REFERENCE SLIDE -- blijft op het scherm

```
+========================================================+
|  HANDS-ON: SESSIE & BESCHERMDE ROUTES       [REF]      |
|                                                        |
|  STAP 1: User ophalen in layout/navbar                 |
|  // In een Server Component:                           |
|  import { createClient } from '@/lib/supabase/server'  |
|                                                        |
|  const supabase = await createClient()                 |
|  const { data: { user } } = await                      |
|    supabase.auth.getUser()                             |
|                                                        |
|  STAP 2: Conditional rendering                         |
|  {user ? (                                             |
|    <div>                                               |
|      <span>{user.email}</span>                         |
|      <LogoutButton />                                  |
|    </div>                                              |
|  ) : (                                                 |
|    <a href="/login">Inloggen</a>                       |
|  )}                                                    |
|                                                        |
|  STAP 3: LogoutButton component (Client Component)     |
|  'use client'                                          |
|  import { createClient } from '@/lib/supabase/client'  |
|  import { useRouter } from 'next/navigation'           |
|                                                        |
|  export function LogoutButton() {                      |
|    const router = useRouter()                          |
|    const supabase = createClient()                     |
|                                                        |
|    async function handleSignOut() {                    |
|      await supabase.auth.signOut()                     |
|      router.push('/login')                             |
|    }                                                   |
|                                                        |
|    return (                                            |
|      <button onClick={handleSignOut}>Uitloggen</button>|
|    )                                                   |
|  }                                                     |
|                                                        |
|  STAP 4: Redirect na logout naar /login                |
|  router.push('/login') in handleSignOut                |
|                                                        |
|  STAP 5: Testen                                        |
|  [x] Ga naar / zonder login -> redirect naar /login    |
|  [x] Log in -> je ziet je email in de navbar           |
|  [x] Klik uitloggen -> redirect naar /login            |
|  [x] Na uitloggen kun je niet bij / komen              |
+========================================================+
```

**Kernpunten:**
- `getUser()` op de server om de huidige gebruiker op te halen
- Conditional rendering: toon email + logout als ingelogd, anders login-link
- LogoutButton is een Client Component (interactief)
- `signOut()` verwijdert de sessie, daarna redirect naar /login
- Middleware handelt de bescherming af: geen sessie = redirect naar /login

**Spreektekst:**
- "Nu gaan we de sessie zichtbaar maken in de UI."
- "Stap 1: in je layout of navbar haal je de user op. Let op: dit is een Server Component, dus gebruik de server client."
- "supabase.auth.getUser() geeft de huidige ingelogde user terug, of null als niemand ingelogd is."
- "Stap 2: conditional rendering. Als er een user is, toon de email en een logout button. Anders toon een link naar /login."
- "Stap 3: de LogoutButton moet een Client Component zijn, want hij heeft een onClick nodig."
- "De handleSignOut functie roept supabase.auth.signOut() aan. Dat verwijdert de sessie cookie."
- "Stap 4: na het uitloggen stuur je de gebruiker terug naar /login met router.push."
- "Stap 5: testen! Open een incognito venster en ga naar de homepage. Je zou geredirect moeten worden naar /login."
- "Log in, en je ziet je email in de navbar. Klik uitloggen, en je gaat terug naar /login."
- "Als dit allemaal werkt, heb je een werkend auth-systeem!"

---

### Slide 14: Hands-on -- Basis RLS (REFERENCE SLIDE)
**Timing:** 11:10 - 11:25 (15 min)

**Titel:** Hands-on -- Basis RLS
**Ondertitel:** REFERENCE SLIDE -- blijft op het scherm

```
+========================================================+
|  HANDS-ON: BASIS RLS                        [REF]      |
|                                                        |
|  Ga naar Supabase Dashboard > SQL Editor               |
|                                                        |
|  STAP 1: Enable RLS op polls                           |
|  ALTER TABLE polls ENABLE ROW LEVEL SECURITY;          |
|                                                        |
|  STAP 2: Enable RLS op options                         |
|  ALTER TABLE options ENABLE ROW LEVEL SECURITY;        |
|                                                        |
|  STAP 3: Iedereen mag polls LEZEN                      |
|  CREATE POLICY "Iedereen mag polls lezen"              |
|  ON polls FOR SELECT                                   |
|  TO public                                             |
|  USING (true);                                         |
|                                                        |
|  STAP 4: Alleen ingelogde users mogen polls AANMAKEN   |
|  CREATE POLICY "Ingelogde users mogen polls aanmaken"  |
|  ON polls FOR INSERT                                   |
|  TO authenticated                                      |
|  WITH CHECK (true);                                    |
|                                                        |
|  STAP 5: Iedereen mag options LEZEN                    |
|  CREATE POLICY "Iedereen mag options lezen"            |
|  ON options FOR SELECT                                 |
|  TO public                                             |
|  USING (true);                                         |
|                                                        |
|  STAP 6: Ingelogde users mogen options AANMAKEN        |
|  CREATE POLICY "Ingelogde users mogen options maken"   |
|  ON options FOR INSERT                                 |
|  TO authenticated                                      |
|  WITH CHECK (true);                                    |
|                                                        |
|  STAP 7: Ingelogde users mogen stemmen (UPDATE)        |
|  CREATE POLICY "Ingelogde users mogen stemmen"         |
|  ON options FOR UPDATE                                 |
|  TO authenticated                                      |
|  USING (true)                                          |
|  WITH CHECK (true);                                    |
|                                                        |
|  STAP 8: Testen -- app werkt als ingelogd             |
|  [x] Polls laden nog steeds                            |
|  [x] Je kunt nog steeds een poll aanmaken              |
|  [x] Je kunt nog steeds stemmen                        |
|                                                        |
|  STAP 9: Testen -- zonder login geen schrijfrechten    |
|  [x] Polls zijn zichtbaar (SELECT = public)            |
|  [x] Poll aanmaken FAALT (INSERT = authenticated)      |
|  [x] Stemmen FAALT (UPDATE = authenticated)            |
+========================================================+
```

**Kernpunten:**
- RLS moet per tabel ingeschakeld worden met `ALTER TABLE ... ENABLE ROW LEVEL SECURITY`
- Na het inschakelen van RLS is ALLES geblokkeerd totdat je policies aanmaakt
- `TO public` = iedereen (ook niet ingelogd)
- `TO authenticated` = alleen ingelogde users
- `USING (true)` = geen extra voorwaarden (alle rijen)
- `WITH CHECK (true)` = geen extra voorwaarden bij schrijven
- Altijd testen: werkt het als ingelogd? Faalt het zonder login?

**Spreektekst:**
- "De laatste stap: Row Level Security! Open de SQL Editor in je Supabase Dashboard."
- "Stap 1 en 2: enable RLS op beide tabellen. LET OP: zodra je dit doet, is alles geblokkeerd. Je app zal even niet werken totdat je policies toevoegt."
- "Stap 3: de eerste policy. Iedereen mag polls lezen. FOR SELECT, TO public, USING true. Public betekent iedereen, ook niet-ingelogde gebruikers."
- "Stap 4: alleen ingelogde users mogen polls aanmaken. FOR INSERT, TO authenticated. Authenticated betekent: je moet een geldige sessie hebben."
- "Stap 5 en 6: hetzelfde voor de options tabel. Iedereen mag lezen, alleen ingelogde users mogen aanmaken."
- "Stap 7: ingelogde users mogen stemmen. Stemmen is een UPDATE operatie -- je update het votes veld."
- "Stap 8: test als ingelogde user. Alles zou moeten werken zoals voorheen."
- "Stap 9: test zonder login. Open een incognito venster. Polls zijn zichtbaar -- want SELECT is public. Maar een poll aanmaken of stemmen? Dat faalt nu. Precies wat we willen!"
- "Gefeliciteerd! Je database is nu beveiligd."

---

### Slide 15: Samenvatting & Huiswerk
**Timing:** 11:45 - 12:00 (15 min)

**Titel:** Samenvatting & Huiswerk
**Ondertitel:** Wat hebben we geleerd?

```
+========================================================+
|  SAMENVATTING                                          |
|                                                        |
|  5 KEY TAKEAWAYS:                                      |
|                                                        |
|  1. Authenticatie = wie ben je?                         |
|     Autorisatie = wat mag je?                           |
|                                                        |
|  2. Supabase Auth regelt login/registratie              |
|     Email/password, magic link, of OAuth               |
|                                                        |
|  3. Sessies werken via JWT tokens in cookies            |
|     Middleware refresht de sessie automatisch           |
|                                                        |
|  4. Twee clients: browser (client) + server             |
|     @supabase/ssr handelt cookies af                   |
|                                                        |
|  5. RLS beveiligt je database met policies              |
|     public = iedereen | authenticated = ingelogd       |
|                                                        |
|  ---------------------------------------------------- |
|                                                        |
|  HUISWERK:                                             |
|  [1] Bedenk je eindexamenopdracht (welke app?)         |
|  [2] Maak een lijstje van je tabellen + kolommen       |
|  [3] Bedenk welke RLS policies je nodig hebt           |
|  [4] Optioneel: voeg user_id kolom toe aan polls       |
|      en maak een policy: alleen eigen polls deleten    |
|                                                        |
|  VOLGENDE LES:                                         |
|  Bouwen aan je eindexamenopdracht!                     |
+========================================================+
```

**Kernpunten:**
- Vijf key takeaways die alles van vandaag samenvatten
- Authenticatie vs autorisatie: twee aparte concepten
- Supabase Auth: drie methodes, wij gebruikten email/password
- Sessies: JWT tokens, cookies, middleware
- Twee Supabase clients: browser en server
- RLS: policies per tabel, per operatie, public vs authenticated
- Huiswerk: eindexamenopdracht bedenken en tabellen plannen

**Spreektekst:**
- "Laten we samenvatten wat we vandaag geleerd hebben."
- "Takeaway 1: authenticatie is wie ben je, autorisatie is wat mag je. Twee aparte dingen."
- "Takeaway 2: Supabase Auth regelt de authenticatie voor ons. We gebruikten email en wachtwoord, en als bonus magic links."
- "Takeaway 3: sessies werken via JWT tokens die in cookies worden opgeslagen. De middleware refresht ze automatisch."
- "Takeaway 4: in Next.js heb je twee Supabase clients nodig. Een voor de browser en een voor de server. Het @supabase/ssr package maakt dit mogelijk."
- "Takeaway 5: RLS beveiligt je database met policies. Public is voor iedereen, authenticated is alleen voor ingelogde users."
- "Nu het huiswerk. Dit is belangrijk voor de eindexamenopdracht."
- "Nummer 1: bedenk welke app je wilt bouwen. Kies iets dat je leuk vindt en dat haalbaar is."
- "Nummer 2: maak een lijstje van de tabellen die je nodig hebt en welke kolommen ze hebben."
- "Nummer 3: bedenk welke RLS policies je nodig hebt. Wie mag wat lezen en schrijven?"
- "Nummer 4 is optioneel: voeg een user_id kolom toe aan de polls tabel, zodat je kunt bijhouden wie een poll heeft aangemaakt. Dan kun je een policy maken dat je alleen je eigen polls mag verwijderen."
- "Volgende les gaan we bouwen aan jullie eindexamenopdracht. Zorg dat je een plan hebt!"
- "Goed gedaan vandaag. Vragen? Anders zie ik jullie volgende week!"
