chore(auth): Validator interface and registry seam

📝 Summary

Lands Step 2 of the S08 authentication-stub plan: the foundational seam under internal/auth/ that the remaining steps consume. No middleware, no dispatch table, no handlers — only the contract surface.

  • internal/auth/auth.goScheme (string-typed: SchemeBearer = "Bearer", SchemeBasic = "Basic"), the Validator interface (Validate(ctx, secret.Secret) error), sentinel errors ErrTokenMissing / ErrTokenInvalid, and the ServiceName = "artifact-registry" constant.
  • internal/auth/registry.goValidatorFactory type plus the package-level Register / MustResolve seam. MustResolve panics on three contract violations: no factory registered (the build-tag fence's runtime backstop per S08 spec section Build-tag fence), the factory returns an error, and the factory returns a nil Validator with a nil error. Register panics on double-register or nil factory.
  • internal/auth/authtest/fake.goFake(tb testing.TB, tok string) Validator test helper using plain string equality. Calls tb.Fatalf when tok is empty so a caller that builds Fake from a zero-valued struct field fails loudly at the call site instead of silently authenticating an empty credential. Used by downstream middleware, handler, and bootstrap tests; constant-time semantics belong in the production validator.
  • scripts/check-no-authtest-in-prod.sh + .gitlab-ci.yml job lint:no-authtest-in-prod — static fence rejecting non-test imports of internal/auth/authtest, so Fake.Validate's plain == cannot reach a production binary.

The 404 fallback and the 405 response in the dispatch middleware and /v2/auth/token handler will call internal/transport.WriteError (shipped via !261 (merged)) directly, so this MR no longer ships its own envelope writer.

Spec: S08 Authentication (stub). The plan's Step 2 entry has the full file list.

This step is a sibling-parallel enabler: the dispatch middleware, the /v2/auth/token handler, and the bootstrap validator each import this seam without depending on one another.

📏 MR size

Category Files Lines Share Review burden
Hand-written Go production (auth.go, registry.go, authtest/fake.go) 3 +237 31% High — core seam
Hand-written Go tests (*_test.go) 3 +382 50% High — covers every panic + behavior bullet from the plan's Acceptance, plus the unwrap-contract, panic-message-substring assertions, nil-Validator guard, wire-token literals, and empty-tok Fatalf contract added during review
CI fence (scripts/check-no-authtest-in-prod.sh, .gitlab-ci.yml) 2 +56 7% Low
Plan housekeeping (docs/plans/2026-05-12-authentication-stub.md) 1 +93 / -42 12% Low — Step 2/3/4 entries aligned with what shipped
Total 9 +768 / -42 100%

Headline: ~50% of the diff is tests against the seam — every panic condition on Register / MustResolve (with substring assertions on the recovered value), the sentinel %w + errors.Is unwrap contract, and the Fake's match/mismatch/edge cases.

📦 Commits in this MR

Squashed on merge. Pre-squash history (most recent first):

Order Commit Purpose
9 chore(auth): address S08 Step 2 second-round review feedback Second pass folding in the second-round blocking threads (fence hardening, nil-Validator guard in MustResolve, Fake(tb testing.TB, ...) signature, wire-token literal pins, ServiceName tense, plan-ref removal across the package) plus a self-review polish (defensive return nil after Fatalf, compile-time Validator interface checks)
8 chore(auth): address S08 Step 2 review feedback First pass folding in the initial blocking threads, the Duo recommendations, and the AppSec advisory
7 docs(plans): record !266 in S08 Step 2 Status row Plan housekeeping
6 docs(plans): list Step 2 test files in S08 auth-stub plan Plan housekeeping — mirrors what shipped
5 refactor(auth): drop "returned error" from MustResolve panic message Aligns the panic phrasing with go-style.md error-context guidance
4 docs(auth): drop stale post-S01-merge framing from seam doc comments Doc-comment tidy after S01 transport merged
3 refactor(auth): simplify Step 2 seam per code-simplifier pass Post-implementation simplification pass
2 chore(auth): Validator interface and registry seam Implementation that satisfies the tests below
1 test(auth): Validator interface and registry seam Tests authored first (panic-skeleton pattern from the implement-step skill)

🧪 Test plan

  • CI green on go test ./internal/auth/... (registry panics with auth:-prefix substring assertions for missing-factory, double-register, nil-factory, factory-error-surfaces, and nil-Validator cases; sentinel %w + errors.Is unwrap contract; sentinel-error distinctness; Scheme literal wire-token pins; Fake match / mismatch / prefix / suffix coverage; empty-tok Fatalf contract via a recording TB).
  • CI green on golangci-lint (including testifylint), the new lint:no-authtest-in-prod job, and the pre-commit hook stack (go-fmt, go-imports, go-mod-tidy, shellcheck, shfmt).
  • Reviewer sanity: imports resolve only against stdlib, LabKit v2/secret, testify, and the auth package itself.
Edited by Moaz Khalifa

Merge request reports

Loading
Loading