Add auth: OIDC + local-admin, sessions, CSRF
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_endpointdiscovery 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_URLis explicitlyhttp://(dev escape hatch, safe default otherwise).RequireAuthwraps 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)