Turn on protection — and hit the 403
First the dependency:
go get github.com/gorilla/csrf
Three files as tabs, in dependency order: config.go → main.go → router.go — a secret to key the token, the derived key, and the middleware itself. All three are modifications.
1. internal/config/config.go (modify) — a SessionSecret, read from SESSION_SECRET. In dev it falls back to a fixed insecure value so things work out of the box; in prod it's required (a guard returns an error if it's missing). Everything else is carried forward.
2. cmd/signflow/main.go (modify) — derive a stable 32-byte key from the secret, so there's only one secret to manage:
csrfKey := sha256.Sum256([]byte("csrf:" + cfg.SessionSecret))
// ...
Handler: h.Router(staticFS, csrfKey[:]), // ← Router now takes the key
3. internal/handlers/router.go (modify) — the protection itself:
csrfMW := csrf.Protect(
csrfKey,
csrf.Secure(h.Cfg.IsProd()), // Secure cookie only in prod
csrf.FieldName("gorilla.csrf.Token"), // the hidden field forms will carry
)
r.Use(csrfMW)
That's it — CSRF is now enforced on every POST.
Verify — and watch it break. Regenerate and run, then try to log in:
go run ./cmd/signflow
Open http://localhost:8080/login, enter your credentials, submit. Instead of your dashboard:
Forbidden - referer not supplied
A 403. On your own login form. With correct credentials. And the message — "referer not supplied" — tells you nothing about what's actually wrong. This is the error every Go developer hits once and burns an afternoon on.
Confirm it from the command line too:
$ curl -si -X POST localhost:8080/login -d 'email=a@b.co&password=x' | head -1
HTTP/1.1 403 Forbidden
Don't fix it yet. Sit with it for a second. Your form has no token field, and you're on http:// — but the error doesn't mention tokens at all; it complains about the referer. That's the clue. The next step explains exactly why, and fixes it in two moves.