# Les 9 — Supabase Auth ## Docenttekst **Les:** 9 van 18 **Onderwerp:** Supabase Authentication (signUp, signIn, signOut, middleware, RLS) **Duur:** 120 minuten **Vorige les:** Les 8 — Students hebben Supabase gekoppeld, /create pagina werkend, Server Component patroon, polls database --- ## Leerdoelen - Authenticatie vs autorisatie begrijpen - Supabase Auth functies gebruiken: signUp, signInWithPassword, signOut, getUser - Server client (SSR) vs Browser client onderscheiden - Middleware voor session refresh implementeren - Authenticated Navbar bouwen met getUser - Row Level Security (RLS) voor authenticated users toepassen --- ## Lesopbouw & Timing ### 09:00–09:10 | Welkom + Terugblik (10 min) **Slides:** 1, 2, 3 Ik start de les. Korte recap van Les 8: - Supabase project aangemaakt - NEXT_PUBLIC_SUPABASE_URL en ANON_KEY in .env - /create pagina with VoteForm component - Polls tabel in database met votes - "Na vandaag kunnen jullie je app beveiligen met authenticatie" **Planning tonen (slide 3):** - 09:10–10:00: Uitleg Auth concepten + Demo - 10:00–10:15: Samen Middleware + Auth Callback bouwen - 10:15–10:30: Pauze - 10:30–11:30: Zelf Doen (signup, login, logout, Navbar) - 11:30–11:45: Vragen - 11:45–12:00: Huiswerk + Afsluiting --- ### 09:10–10:00 | Deel 1a: Uitleg Auth Concepten (50 min) **Slides:** 4, 5, 6 #### 09:10 | Slide 4: Wat is Auth? **Vertel:** "Authenticatie is: wie ben jij? Login, password, je identiteit bewijzen. Autorisatie is: wat mag je doen? Wie mag polls maken? Dit regelen we later met RLS. Supabase Auth beheert alles: signUp, login, sessies, JWT tokens." **Demo:** Open https://supabase.com/dashboard - Klik project → Authentication → Providers → Email - Laat zien: Disable Email Confirmations is AAN (sneller testen) - Zeg: "Students zien zelf deze checkbox na Le 9" #### 09:20 | Slide 5: Auth Functies **Vertel:** "Vier kern functies in Supabase Auth: 1. signUp({ email, password }) — Nieuw account 2. signInWithPassword({ email, password }) — Inloggen 3. signOut() — Uitloggen 4. getUser() — Wie is ingelogd? Hieronder toon ik hoe we deze gebruiken in Next.js." **Code tonen (slide 5):** ```typescript // signUp const { error } = await supabase.auth.signUp({ email, password }); // signIn const { error } = await supabase.auth.signInWithPassword({ email, password }); // signOut await supabase.auth.signOut(); // getUser (server of browser) const { data: { user } } = await supabase.auth.getUser(); ``` #### 09:30 | Slide 6: Server vs Browser Client **Vertel:** "Supabase Auth werkt in twee omgevingen: - **Server Client** (Node.js, SSR): via cookies, secure - **Browser Client** (React, CSR): via localstorage, minder secure We gebruiken @supabase/ssr package. Dit handelt beide af." **Toon twee code blokken naast elkaar (slide 6):** **Server Client** (middleware, Navbar): ```typescript import { createServerClient } from "@supabase/ssr"; import { cookies } from "next/headers"; export async function createSupabaseServerClient() { 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) { try { cookiesToSet.forEach(({ name, value, options }) => cookieStore.set(name, value, options) ); } catch { } }, }, } ); } ``` **Browser Client** (signup, login, logout): ```typescript import { createBrowserClient } from "@supabase/ssr"; export function createSupabaseBrowserClient() { return createBrowserClient( process.env.NEXT_PUBLIC_SUPABASE_URL!, process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY! ); } ``` **Zeg:** "Cookies zijn beveiligd. localStorage in browser kan hack worden. Daarom: server client voor getUser in Navbar, browser client voor login forms." **📌 Slide 6 referentie voor Middleware:** Middleware zorgt dat de session word gerefresht op elke request: ```typescript // middleware.ts export async function middleware(request: NextRequest) { let supabaseResponse = NextResponse.next({ request }); const supabase = createServerClient(...); await supabase.auth.getUser(); return supabaseResponse; } ``` "Dit zorgt dat je Session JWT token altijd up-to-date is." --- ### 10:00–10:15 | Deel 1b: Samen Coderen (15 min) #### Stap 1: npm install ```bash npm install @supabase/ssr ``` #### Stap 2: lib/supabase-server.ts aanmaken Voeg dit in (hieronder exact): ```typescript import { createServerClient } from "@supabase/ssr"; import { cookies } from "next/headers"; export async function createSupabaseServerClient() { 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) { try { cookiesToSet.forEach(({ name, value, options }) => cookieStore.set(name, value, options) ); } catch { } }, }, } ); } ``` #### Stap 3: lib/supabase-browser.ts aanmaken ```typescript import { createBrowserClient } from "@supabase/ssr"; export function createSupabaseBrowserClient() { return createBrowserClient( process.env.NEXT_PUBLIC_SUPABASE_URL!, process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY! ); } ``` #### Stap 4: middleware.ts (root project) ```typescript import { createServerClient } from "@supabase/ssr"; import { NextResponse, type NextRequest } from "next/server"; export async function middleware(request: NextRequest) { let supabaseResponse = NextResponse.next({ request }); const supabase = createServerClient( process.env.NEXT_PUBLIC_SUPABASE_URL!, process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!, { cookies: { getAll() { return request.cookies.getAll(); }, setAll(cookiesToSet) { cookiesToSet.forEach(({ name, value }) => request.cookies.set(name, value)); supabaseResponse = NextResponse.next({ request }); cookiesToSet.forEach(({ name, value, options }) => supabaseResponse.cookies.set(name, value, options) ); }, }, } ); await supabase.auth.getUser(); return supabaseResponse; } export const config = { matcher: ["/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)"], }; ``` #### Stap 5: auth/callback route `app/auth/callback/route.ts`: ```typescript import { NextResponse } from "next/server"; import { createSupabaseServerClient } from "@/lib/supabase-server"; export async function GET(request: Request) { const { searchParams, origin } = new URL(request.url); const code = searchParams.get("code"); if (code) { const supabase = await createSupabaseServerClient(); await supabase.auth.exchangeCodeForSession(code); } return NextResponse.redirect(origin); } ``` **Zeg:** "Dit is standaard Supabase/Next.js boilerplate. Niet allemaal letterlijk begrijpen. Focus op: server vs browser client." --- ### 10:15–10:30 | Pauze **Slide 7** --- ### 10:30–11:30 | Deel 2: Zelf Doen (60 min) **Slide 8** Students bouwen nu zelf: 1. app/signup/page.tsx 2. app/login/page.tsx 3. components/LogoutButton.tsx 4. components/Navbar.tsx (met getUser) 5. Uitloggen in layout.tsx **Reference code:** #### app/signup/page.tsx ```typescript 'use client' import { createSupabaseBrowserClient } from "@/lib/supabase-browser"; import { useRouter } from "next/navigation"; import { useState } from "react"; import Link from "next/link"; export default function SignUp() { const [email, setEmail] = useState(""); const [password, setPassword] = useState(""); const [message, setMessage] = useState(""); const [loading, setLoading] = useState(false); const router = useRouter(); const supabase = createSupabaseBrowserClient(); const handleSignUp = async (e: React.FormEvent) => { e.preventDefault(); setLoading(true); setMessage(""); const { error } = await supabase.auth.signUp({ email, password }); if (error) { setMessage(error.message); } else { setMessage("Account aangemaakt!"); router.push("/login"); } setLoading(false); }; return (
{message}
}Al een account? Inloggen
{message}
}Nog geen account? Registreren