SCRUM-209 — admin-auth audit endpoint and login/refresh metrics¶
Plan ref: AA-10 (docs/11-admin-plane-plan.md). Closes gap 7 in doc 10.
What exists¶
| Piece | Notes |
|---|---|
GET /admin-auth/audit |
same wire shape as Config's /api/admin/config/audit: {"entries":[{id, at, actor_id, actor_name, source:"admin-auth", action, target, details}], "next_cursor"}; newest first; limit 1..200 (default 50), opaque cursor (base64url {"before":id}), action, actor, from/to (RFC3339). Malformed → 400 validation_failed |
| Auth | requireRole("viewer"): token verified, roles read from the DB row, disabled/missing account → 401 invalid_token, below minimum → 403 insufficient_role. requireAdmin is now requireRole("admin") |
staff_login_total{result} |
success, invalid_credentials (wrong password, unknown email, disabled), locked (already locked or this failure trips the lock), bad_request, error; all at 0 from start-up |
staff_refresh_total{result} |
success (incl. the 30 s grace path), invalid, reuse_detected, error |
Audit actions already written by earlier tickets: login.success, login.locked,
logout, session.reuse_detected, user.invite, user.reset_link,
invite.revoke, user.update, user.onboard, user.password_reset,
root.bootstrap, root.rotate. gateway_dev already forwards /admin-auth/*.
How to verify¶
cd services/admin_auth
gofmt -l . && go vet ./...
ADMIN_AUTH_TEST_DATABASE_URL=postgres://auth_rw:pw@127.0.0.1:5433/admin_auth_test?sslmode=disable \
go test -race -count=1 ./...
internal/server/audit_test.go: login + invite appear as login.success and
user.invite in the shared shape; limit=1 paging walks every row once and ends with
next_cursor:null; each filter; malformed cursor/limit 400; no token 401; disabled
user 401; viewer allowed. metrics_test.go: good login, wrong password, malformed
body → 1 each, other series 0; refresh success and a replay → reuse_detected.
Results at time of writing¶
gofmt,go vet,go test -racewith the DB (7 packages): pass.
How it was built¶
DeepSeek run scoped (Landlock) to services/admin_auth (213 s, ~29k output tokens,
reasoning effort low). Claude review: store query and wire format diffed against
Config's audit; no changes needed.