Theme
/
Theory

The auth plan — sessions not JWT; bcrypt

The app has a users table with a password_hash column nothing fills yet. This lesson starts the auth phase: by the end, someone can register an account and the server will remember them across requests. That's two problems — storing a password safely and remembering who's logged in — and each has a deliberate answer.

Passwords: bcrypt, cost 12. You never store a password; you store a one-way hash. We use bcrypt (golang.org/x/crypto/bcrypt) at cost 12 (~250ms per hash — slow on purpose, to make brute force expensive). Why bcrypt over argon2id? For a teaching codebase it has the smaller, harder-to-misuse surface: one tuning knob instead of three, and the cost baked into the hash string. The honest tradeoff is stated in the code — argon2id is OWASP's first pick for new systems, and bcrypt silently truncates input past 72 bytes. We defuse that last one by capping password length at 64, so nothing is ever silently cut. (Never cap below the truncation point by accident — 72 is the cliff, 64 is the fence in front of it.)

Remembering who's logged in: a server-side session — NOT a JWT. This is the deliberate counterpoint to the Pica mobile app, and lesson 14 is the whole argument. The shape:

  • The browser holds one thing: an opaque random token in a cookie. No claims, no user id, nothing readable.
  • The real session lives in a sessions row in Postgres.
  • We store only the SHA-256 hash of the token, never the token itself — same trick as a password. A database dump hands an attacker no usable sessions.
  • The cookie is HttpOnly (JavaScript can't read it → an XSS bug can't steal it), SameSite=Lax (not sent on cross-site POSTs → basic CSRF resistance), and Secure in production (HTTPS only).

The single biggest consequence — and the thing a JWT cannot do — is that logout is one DELETE. Because the session is state we own, we can revoke it instantly. You'll build that in lesson 5.

This lesson:

  1. auth/password.go (bcrypt), the sessions table + its queries, and the session.Manager.
  2. The register form and handler, wired end to end.

By the end: register a real account, and see the session cookie set with its flags — and a sessions row storing only a hash.

Note — CSRF is coming, on purpose. These first forms POST with no CSRF protection. That's deliberate: lesson 6 adds gorilla/csrf, and you'll watch it reject this very form with a baffling error before you learn what CSRF actually defends. Feel the app work first; armor it second.