Mutations, APIs and security · Lesson 12 of 16
Authentication and protecting pages
Sessions in cookies, checking at every layer, and Supabase Auth.
Authentication = "who are you?"; authorization = "what may you do?". In Next.js the session usually lives in a secure cookie (httpOnly, secure, sameSite), so the server can read it in the proxy, pages and actions, while browser JavaScript can't steal it.
// lib/auth.ts: one helper, used everywhere on the server
import "server-only"
import { redirect } from "next/navigation"
import { createClient } from "@/utils/supabase/server"
export async function requireUser(role?: "instructor" | "admin") {
const supabase = await createClient()
const { data: { user } } = await supabase.auth.getUser() // verified with Supabase, not just decoded
if (!user) redirect("/login")
if (role) {
const { data: profile } = await supabase.from("profiles").select("role").eq("id", user.id).single()
if (profile?.role !== role && profile?.role !== "admin") redirect("/dashboard")
}
return user
}
// app/instructor/page.tsx
export default async function InstructorPage() {
const user = await requireUser("instructor")
return <h1>Welcome back, {user.email}</h1>
}| Layer | What it does | Enough alone? |
|---|---|---|
proxy.ts | quick redirect for signed-out users | no |
page / layout | requireUser() before rendering | no (actions are called directly) |
server action / route | requireUser() + permission on this resource | almost |
database (RLS) | rows only readable/writable by their owner | the last line of defence |
Defense in depth: each layer assumes the one before may fail.
Quick question
The /admin page is protected only in the proxy. The deleteUser server action it uses doesn't check the role. Is that safe?
Exercise
Write canEdit(user, project): a user may edit when they own the project (project.ownerId === user.id) or their role is admin. A null user may not. Print the four results.
true false true false
function canEdit(user, project) {
return true
}
const project = { id: 7, ownerId: "u1" }
console.log(canEdit({ id: "u1", role: "student" }, project))
console.log(canEdit({ id: "u2", role: "student" }, project))
console.log(canEdit({ id: "u3", role: "admin" }, project))
console.log(canEdit(null, project))Learn more from
Played 0 of 2 games
Sign in to save your progress and earn XP.