Files
novi-lessons/Les18-Supabase-Auth-RLS/Les18-Huiswerk.md
2026-06-03 16:58:25 +02:00

333 lines
8.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Les 18 — Huiswerk
## Multi-user Tasks app met RLS + social login + AUTH.md
**Vak:** AI-Assisted Development
**Deadline:** Voor eindopdracht-inlevering (4 weken)
**Inleveren:** GitHub repo + `AUTH.md` in root + Vercel productie URL
---
## Doel
Bouw je auth-demo uit naar productie-niveau: tasks per user met RLS, GitHub social login, en deploy. Eindopdracht-ready.
---
## Onderdeel A — Tasks tabel + RLS (verplicht)
### A1 — Schema
In Supabase SQL Editor:
```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 tasks" on tasks
for select using (auth.uid() = user_id);
create policy "insert own tasks" on tasks
for insert with check (auth.uid() = user_id);
create policy "update own tasks" on tasks
for update using (auth.uid() = user_id);
create policy "delete own tasks" on tasks
for delete using (auth.uid() = user_id);
```
### A2 — Tasks UI
`app/page.tsx` — extend met tasks lijst + form:
```tsx
import { createClient } from "@/utils/supabase/server";
import { redirect } from "next/navigation";
import { revalidatePath } from "next/cache";
async function addTask(formData: FormData) {
"use server";
const supabase = await createClient();
await supabase.from("tasks").insert({ text: formData.get("text") as string });
revalidatePath("/");
}
async function toggleTask(id: number, done: boolean) {
"use server";
const supabase = await createClient();
await supabase.from("tasks").update({ done: !done }).eq("id", id);
revalidatePath("/");
}
async function deleteTask(id: number) {
"use server";
const supabase = await createClient();
await supabase.from("tasks").delete().eq("id", id);
revalidatePath("/");
}
export default async function Home() {
const supabase = await createClient();
const { data: { user } } = await supabase.auth.getUser();
if (!user) redirect("/login");
const { data: tasks } = await supabase
.from("tasks")
.select("*")
.order("created_at", { ascending: false });
return (
<main className="p-8 max-w-2xl mx-auto">
<h1 className="text-2xl mb-4">Tasks van {user.email}</h1>
<form action={addTask} className="flex gap-2 mb-6">
<input name="text" className="border p-2 flex-1" placeholder="Nieuwe taak..." />
<button className="bg-blue-600 text-white px-4 py-2 rounded">+</button>
</form>
<ul className="space-y-2">
{tasks?.map((t) => (
<li key={t.id} className="flex items-center gap-2 p-2 border rounded">
<form action={toggleTask.bind(null, t.id, t.done)}>
<button className={t.done ? "line-through" : ""}>{t.text}</button>
</form>
<form action={deleteTask.bind(null, t.id)} className="ml-auto">
<button className="text-red-500">×</button>
</form>
</li>
))}
</ul>
</main>
);
}
```
### Eisen
- [ ] `tasks` tabel met RLS aan
- [ ] 4 policies: select / insert / update / delete
- [ ] UI: add, toggle, delete tasks werkt
- [ ] In incognito met ander account: andere tasks zichtbaar (geen vermenging)
---
## Onderdeel B — Social login (GitHub) (verplicht)
### B1 — GitHub OAuth App
1. github.com → Settings → Developer settings → OAuth Apps → New
2. Application name: jouw app
3. Homepage URL: `http://localhost:3000` (dev) of `https://yourapp.vercel.app` (prod)
4. Callback URL: `https://your-project.supabase.co/auth/v1/callback`
5. Save, generate Client Secret
### B2 — Configure in Supabase
1. Dashboard → Auth → Providers → GitHub
2. Enable + plak Client ID + Secret
### B3 — Button toevoegen
In `app/login/page.tsx`:
```tsx
async function signInWithGitHub() {
const supabase = createClient();
await supabase.auth.signInWithOAuth({
provider: "github",
options: { redirectTo: `${location.origin}/auth/callback` },
});
}
// In render:
<button onClick={signInWithGitHub} className="border px-4 py-2 mt-3 rounded">
Of: Login met GitHub
</button>
```
### Eisen
- [ ] GitHub OAuth App aangemaakt
- [ ] Supabase configured
- [ ] Klik op GitHub button → redirect → ingelogd
- [ ] Tasks werken met GitHub-account
---
## Onderdeel C — Deploy naar Vercel (verplicht)
Combineert Les 14/15:
1. Push naar GitHub
2. Vercel: import repo
3. Add env vars: `NEXT_PUBLIC_SUPABASE_URL`, `NEXT_PUBLIC_SUPABASE_ANON_KEY`
4. Deploy
5. Update Supabase Auth → URL Configuration:
- **Site URL:** `https://yourapp.vercel.app`
- **Redirect URLs:** `https://yourapp.vercel.app/**`, `http://localhost:3000/**`
6. Update GitHub OAuth App callback URL to productie
### Eisen
- [ ] App live op Vercel
- [ ] Magic link werkt op productie URL
- [ ] GitHub login werkt op productie URL
- [ ] Tasks per user werken op productie
---
## Onderdeel D — `AUTH.md` documentatie (verplicht)
In repo-root.
### Sectie 1 — Auth methoden
Welke auth methoden ondersteund:
- Magic link (default)
- GitHub OAuth
- (eventuele extra's)
### Sectie 2 — RLS policies
Lijst je policies + waarom:
```markdown
**Table: tasks**
- `select own tasks` — users zien alleen eigen tasks
- `insert own tasks` — voorkomt inserten op andermans naam
- `update own tasks` — alleen eigen toggle
- `delete own tasks` — alleen eigen delete
```
### Sectie 3 — Test van multi-user isolation
Bewijs dat RLS werkt:
- Screenshot van Account A's tasks
- Screenshot van Account B's tasks (incognito)
- Bewijs: account A's tasks staan NIET in account B's lijst
### Sectie 4 — Deployment checklist
Wat je moest aanpassen voor productie:
- Site URL in Supabase
- Redirect URLs in Supabase
- GitHub OAuth callback URL
- Env vars in Vercel
### Sectie 5 — Eindopdracht-architectuur (~250 woorden)
Concrete plan voor je eigen eindopdracht:
- App naam + doel
- User-flows (signup, login, kernfeature)
- Welke tabellen + RLS policies
- AI feature(s) en hoe ze user-context gebruiken
- Externe APIs (uit Les 17)
- Tijd-plan (4-6 weken)
### Vorm
- Max 800 woorden
- Screenshots zijn waardevol
- Concrete plan, geen vaagheid
---
## Bonus (optioneel)
### Bonus 1 — Email + password naast magic link
Voeg klassieke signup/login toe. Combine met magic link.
### Bonus 2 — Profile pagina
`/profile` — user kan naam, avatar URL, etc. instellen. Aparte `profiles` tabel met RLS.
### Bonus 3 — Team feature
Multi-user binnen team. Aparte tabel `teams`, junction `team_members`. RLS policies die team-leden toelaten elkaars data te zien.
### Bonus 4 — AI integration met user context
Voeg een AI chat toe waar de user-tasks deel zijn van de context. AI weet wat jouw tasks zijn (door RLS), kan helpen prioriteren.
---
## Inleveren
1. **GitHub repo URL** in Brightspace
2. **Vercel productie URL** in Brightspace
3. **`AUTH.md`** in repo-root
4. **Test-account credentials** voor docent (email of GitHub)
5. **2-3 screenshots** van multi-user test
---
## Beoordeling
| Criterium | Punten |
|-----------|--------|
| A — Tasks + RLS werkt (4 policies) | 3 |
| B — GitHub OAuth werkt | 2 |
| C — Deployed op Vercel + auth werkt op prod | 2 |
| D — AUTH.md compleet (5 secties) | 2 |
| Multi-user isolation bewezen | 1 |
| **Totaal** | **10** |
Voldoende = 6+. Bonus telt mee.
---
## Tijd-indicatie
| Onderdeel | Tijd |
|-----------|------|
| A — Tasks + RLS | 50 min |
| B — GitHub OAuth | 25 min |
| C — Deploy | 20 min |
| D — AUTH.md | 35 min |
| **Totaal** | **~2 uur 10 min** |
---
## Veelvoorkomende valkuilen
| Probleem | Oplossing |
|----------|-----------|
| Tasks van andere users zichtbaar | RLS niet aan, of policy `using (true)` |
| Insert faalt met "row violates" | RLS check faalt — check `with check (auth.uid() = user_id)` + default |
| OAuth redirect 404 | Site URL niet bijgewerkt voor productie |
| Magic link werkt lokaal niet prod | Site URL in Supabase staat nog op localhost |
| Cookies werken niet | Middleware draait niet — check matcher en path |
| `auth.uid()` returnt null | User niet ingelogd — check session in Server Component |
---
## Tips
- **Test isolation in incognito** — alleen daar ben je écht "another user"
- **RLS error messages** kunnen vaag zijn — log policy + auth.uid() in Postgres function voor debug
- **Productie env vars** — vergeet niet je Vercel env vars te zetten
- **Eindopdracht-koppeling:** dit huiswerk is je startpunt — bouw eindopdracht hier op
- **Veel succes met de eindopdracht!**
---
## Voor je begint aan de eindopdracht
Als afsluiting:
1. Plan je eindopdracht concreet (Sectie 5 van AUTH.md is de basis)
2. Start een nieuwe repo van scratch óf bouw verder op deze
3. Push klein en vaak — niet één big commit aan eind
4. Vraag hulp tijdig — Slack, mail, office hours
5. Plan tijd in van min 8-10 uur/week voor 4-6 weken
**Bedankt voor jullie inzet deze 18 lessen. Veel succes met de eindopdracht!**