# Les 9 — Supabase Auth ## Live Coding Guide voor Docent This is your cheat sheet for the full lesson. Follow the timing in Les09-Docenttekst.md. --- ## DEEL 1A: UITLEG AUTH (09:10–10:00) ### 09:10 | SLIDE 4: Wat is Auth? **Demo:** Open https://supabase.com/dashboard Vertel: "Authenticatie is: wie ben jij? Login en password, je identiteit bewijzen. Autorisatie is: wat mag je doen? Wie mag polls maken? Dit regelen we met RLS policies. Supabase Auth beheert alles: signUp, login, sessies, JWT tokens." **Stap 1:** Dashboard openen, project selecteren **Stap 2:** Klik Authentication → Providers → Email **Stap 3:** Toon: "Disable Email Confirmations" is AAN Vertel: "Deze checkbox is cruciaal voor testen. Normaal zouden users een confirmation email krijgen voordat ze inloggen. Dat slaan we over voor deze les. In productie zet je dit uit." --- ### 09:20 | SLIDE 5: Auth Functies **Vertel:** "Supabase Auth heeft vier kern functies:" **Code tonen (copy-paste in terminal of code editor):** ```typescript // 1. signUp — Nieuw account const { error } = await supabase.auth.signUp({ email: "user@example.com", password: "secure123" }); if (error) console.error(error.message); // 2. signInWithPassword — Inloggen const { error } = await supabase.auth.signInWithPassword({ email: "user@example.com", password: "secure123" }); if (error) console.error(error.message); // 3. signOut — Uitloggen await supabase.auth.signOut(); // 4. getUser — Wie is ingelogd? const { data: { user } } = await supabase.auth.getUser(); console.log(user.email); // "user@example.com" console.log(user.id); // "abc-123-def" ``` Vertel: "Diese vier functies zijn alles wat je nodig hebt. Error handling: altijd checken of `error` null is." --- ### 09:30 | SLIDE 6: Server vs Browser Client **Vertel:** "Supabase Auth werkt op twee plaatsen: 1. **Server (Node.js)** — Secure, via cookies 2. **Browser (React)** — Less secure, via localStorage We gebruiken @supabase/ssr. Dit switcht automatisch." **Show left code block (Server Client):** ```typescript // lib/supabase-server.ts 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 { } }, }, } ); } ``` Vertel: "Server client gebruikt cookies. Cookies kunnen beveiligd worden (httpOnly, secure-only). Dit is safer." **Show right code block (Browser Client):** ```typescript // lib/supabase-browser.ts import { createBrowserClient } from "@supabase/ssr"; export function createSupabaseBrowserClient() { return createBrowserClient( process.env.NEXT_PUBLIC_SUPABASE_URL!, process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY! ); } ``` Vertel: "Browser client werkt in React. localStorage is minder secure (scripts kunnen het uitlezen), maar nodig voor login forms." **Key difference:** "Server component (Navbar) → Server client (getUser) Client component (LoginForm) → Browser client (signUp, signIn, signOut)" --- ## DEEL 1B: SAMEN CODEREN (10:00–10:15) Students volgen mee terwijl je dit live codeert (of ze kopieren uit les09-live-coding-guide.md). ### Stap 1: npm install ```bash npm install @supabase/ssr ``` Wacht tot dit klaar is. ### Stap 2: lib/supabase-server.ts Maak dit bestand aan: ```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 ```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 of project, naast app/) ```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)$).*)"], }; ``` Vertel: "Middleware runt op elke request. `await supabase.auth.getUser()` refresht de session token. Dit zorgt dat je niet uitgelogd wordt als je token expired." ### Stap 5: 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); } ``` Vertel: "Dit is voor OAuth (Google, GitHub). Supabase stuur je hier naartoe na login. De `code` wordt ge-exchanged voor een session. Voor nu: boilerplate, niet essentieel." ### Test middleware Vertel: "Test of middleware werkt: open http://localhost:3000. Je zou geen errors moeten zien. In browser dev tools → Application → Cookies → zoek naar `sb-*`. Die cookies beteken dat middleware goed werkt." --- ## PAUZE (10:15–10:30) --- ## DEEL 2: ZELF DOEN (10:30–11:30) Students bouwen nu zelf. Jij loopt rond, helpt, en toont code op beamer als studenten stuck zijn. ### Zelf Doen Checklist (wat moet elke student doen): - [ ] app/signup/page.tsx - [ ] app/login/page.tsx - [ ] components/LogoutButton.tsx - [ ] components/Navbar.tsx - [ ] app/layout.tsx updated - [ ] Test signup → login → poll maken → logout --- ### 1. app/signup/page.tsx Dit is een 'use client' component met form. Students schrijven dit zelf, maar hier is de reference: ```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