Middleware, and logout = one DELETE
Last lesson you could register and get a session cookie — but the app didn't act on it. This lesson makes sessions real: a "logged in" header, a login page, logout, and a protected dashboard.
Two pieces of middleware. Middleware wraps every request. We add two:
LoadUserruns on every route. It reads the session cookie, resolves it to a user, and stashes that user in the request context. It never blocks anyone — an anonymous request just passes through un-annotated. After it runs, any handler can askUserFrom(r.Context())"who is this?"RequireAuthwraps only protected routes. If there's no user in the context, it redirects to/login. It's how/dashboardbecomes members-only, in one line:pr.Use(h.RequireAuth).
That split — annotate everywhere, guard selectively — is the standard shape of session auth in a server-rendered app.
Login is symmetric with register. Look up the user by email, CheckPassword against the stored bcrypt hash, and on success Sessions.Create — the same call register makes. One subtlety, though:
Enumeration resistance. Login uses one generic error for every failure: unknown email and wrong password both return "Invalid email or password." If "no such user" and "wrong password" gave different messages, an attacker could discover which emails have accounts just by watching the responses. Same principle will guard password reset in lesson 7 (the confirmation is identical whether or not the email exists).
Logout is the whole argument for server-side sessions. It's one line: Sessions.Destroy → DELETE FROM sessions WHERE token_hash = $1. The session is gone — instantly, server-side, unforgeably. A JWT cannot do this. A stateless token is valid until it expires; there's no server record to delete, so "logging out" a JWT means extra machinery (short lifetimes + refresh tokens, or a denylist you have to check on every request). SignFlow revokes with a single DELETE because it owns the state. Hold that thought — lesson 14 makes it the centerpiece.
This lesson:
LoadUser+RequireAuth+ aNavmodel, and the layout header that reflects login state.- Login, logout, and the
RequireAuth-protected dashboard.