feat(auth): backend endpoints for MED-7 + MED-11 + MED-12

Audit 2026-05-10 MED-7 + MED-11 + MED-12 backend halves.

WHAT.

Three new admin-gated endpoints:

  GET    /api/v1/auth/oidc/providers/{id}/jwks-status  (auth.oidc.list)   — MED-7
  GET    /api/v1/auth/users                            (auth.user.read)        — MED-11
  DELETE /api/v1/auth/users/{id}                       (auth.user.deactivate)  — MED-11
  GET    /api/v1/auth/runtime-config                   (auth.role.assign)      — MED-12

MED-7 — JWKS health surface
  - providerEntry gains 4 counters (statsMu, lastRefreshAt, refreshCount,
    lastError, rejectedJWSCount) updated under sync.Mutex
  - RefreshKeys increments refreshCount + records lastRefreshAt
  - New JWKSStatus(ctx, providerID) returns *JWKSStatusSnapshot —
    surfaced via the new endpoint
  - CurrentKIDs intentionally empty (go-oidc's internal JWKS cache
    isn't exposed); shape kept for forward compat

MED-11 — federated-user admin
  - AuthUsersHandler.List with optional ?oidc_provider_id filter
  - AuthUsersHandler.Deactivate sets users.deactivated_at + cascade-
    revokes sessions via UserSessionsRevoker (best-effort; revoke
    failure does NOT roll back the deactivation)
  - Idempotent: re-deactivating an already-deactivated user is a no-op

MED-12 — runtime config
  - AuthRuntimeConfigHandler.Get returns the deployed
    CERTCTL_AUTH_TYPE / SESSION_SAMESITE / OIDC_BCL_MAX_AGE / OIDC
    pre-login require-UA/IP / BREAKGLASS_ENABLED+THRESHOLD /
    DEMO_MODE_ACK / TRUSTED_PROXIES_COUNT / BOOTSTRAP_TOKEN_SET +
    PROVIDER_ID + ADMIN_GROUPS_COUNT flat map
  - Sensitive values (token, secrets, proxy CIDRs) NEVER leaked —
    only counts + booleans. Token presence surfaced as 'set/unset'
  - Gated auth.role.assign (admin-class) so non-admins can't
    enumerate the deployment's auth knobs

cmd/server/main.go wires all three handlers into HandlerRegistry.
internal/api/router/router.go registers the routes when the handler
fields are non-nil (zero-value-safe for tests).

VERIFY.

- go vet ./internal/api/... ./internal/auth/... ./internal/repository/... PASS
- go build ./cmd/server/...                                                PASS
- go test -short -count=1 ./internal/auth/oidc/...                         PASS (4.1s)
- go test -short -count=1 ./internal/api/handler/...                       PASS (4.1s)

GUI halves for MED-7 + MED-11 + MED-12 are the GUI batch (pending).

Refs: cowork/auth-bundles-audit-2026-05-10.md MED-7, MED-11, MED-12
      cowork/auth-bundles-fixes-2026-05-10/HANDOFF.md items 11 14 15
This commit is contained in:
shankar0123
2026-05-11 00:11:07 +00:00
parent e1e43c8924
commit 172b30b8f1
4 changed files with 386 additions and 2 deletions
+37
View File
@@ -303,6 +303,21 @@ type HandlerRegistry struct {
// Optional — when nil the routes are not registered.
AuthBreakglass *handler.AuthBreakglassHandler
// AuthUsers handles the MED-11 federated-user admin surface
// (GET /api/v1/auth/users; DELETE /api/v1/auth/users/{id}).
// Optional — when nil the routes are not registered.
AuthUsers *handler.AuthUsersHandler
// AuthRuntimeConfig handles the MED-12 admin-only runtime
// config read endpoint (GET /api/v1/auth/runtime-config).
// Optional — when nil the route is not registered.
AuthRuntimeConfig *handler.AuthRuntimeConfigHandler
// AuthOIDCJWKSStatus handles the MED-7 per-provider JWKS health
// endpoint (GET /api/v1/auth/oidc/providers/{id}/jwks-status).
// Optional — when nil the route is not registered.
AuthOIDCJWKSStatus *handler.AuthOIDCJWKSStatusHandler
// IntermediateCAs handles the admin-gated CA-hierarchy management
// surface under /api/v1/issuers/{id}/intermediates and
// /api/v1/intermediates/{id}. Rank 8 of the 2026-05-03 deep-
@@ -464,6 +479,28 @@ func (r *Router) RegisterHandlers(reg HandlerRegistry) {
// reachability without persisting.
r.Register("POST /api/v1/auth/oidc/test", rbacGate(reg.Checker, "auth.oidc.create", reg.AuthSessionOIDC.TestProvider))
// Audit 2026-05-10 MED-7 — JWKS health surface.
if reg.AuthOIDCJWKSStatus != nil {
r.Register("GET /api/v1/auth/oidc/providers/{id}/jwks-status",
rbacGate(reg.Checker, "auth.oidc.list", reg.AuthOIDCJWKSStatus.Status))
}
// Audit 2026-05-10 MED-11 — federated-user admin surface.
if reg.AuthUsers != nil {
r.Register("GET /api/v1/auth/users",
rbacGate(reg.Checker, "auth.user.read", reg.AuthUsers.List))
r.Register("DELETE /api/v1/auth/users/{id}",
rbacGate(reg.Checker, "auth.user.deactivate", reg.AuthUsers.Deactivate))
}
// Audit 2026-05-10 MED-12 — auth runtime config read.
// Gated auth.role.assign (admin-class) so non-admins can't
// enumerate the deployment's auth knobs.
if reg.AuthRuntimeConfig != nil {
r.Register("GET /api/v1/auth/runtime-config",
rbacGate(reg.Checker, "auth.role.assign", reg.AuthRuntimeConfig.Get))
}
// Group-mapping CRUD.
r.Register("GET /api/v1/auth/oidc/group-mappings", rbacGate(reg.Checker, "auth.oidc.list", reg.AuthSessionOIDC.ListGroupMappings))
r.Register("POST /api/v1/auth/oidc/group-mappings", rbacGate(reg.Checker, "auth.oidc.edit", reg.AuthSessionOIDC.AddGroupMapping))