Skip to content

Add auth: OIDC + local-admin, sessions, CSRF

Aiho Tehisaru requested to merge feature/auth into trunk

Stacked on !2 (merged) (feature/proxmox-client) — targets that branch until the chain (!1 (merged) -> !2 (merged) -> this) merges in order, then should be retargeted to trunk.

Phase 4 of the build plan: authentication, built and tested before any provisioning capability is reachable over HTTP.

What's here

  • OIDC (authorization-code + PKCE): go-oidc's verifier checks issuer/audience/signature/expiry but explicitly does not check the nonce itself — that's done here against the value saved alongside state/PKCE-verifier in a short-lived store row, since none of it survives the redirect round-trip otherwise. Best-effort end_session_endpoint discovery for real provider-side logout.
  • Local-admin: single operator-configured username + bcrypt hash, one credential pair (not a user table), minimal self-contained login form.
  • Sessions: opaque tokens in an httponly/samesite=lax cookie, Secure unless RAX_BASE_URL is explicitly http:// (dev escape hatch, safe default otherwise). RequireAuth wraps every route except /auth/* and /healthz.
  • CSRF: a synchronizer token minted per-session and checked on every non-GET/HEAD/OPTIONS request.

Notable: real OIDC test coverage, not mocked

internal/auth/oidc_test.go spins up a real fake OIDC provider (RSA key, genuine discovery document + JWKS + token endpoint, RS256-signed JWTs via go-jose) and drives Rax's actual verification code through it — valid login, nonce mismatch, wrong audience, unknown state — not a shortcut around the crypto.

Test plan

  • gofmt -l . clean
  • go vet ./... clean
  • go build ./... succeeds
  • go test ./... — all tests pass (78 total across the module; adds 17 new auth tests + extends store tests for the new csrf_token column)

🤖 Generated with Claude Code

https://claude.ai/code/session_01U9buZPRQy7YXLodkSPFFik

Merge request reports

Loading