Skip to content

feat: admin activity log (logins + config changes) - #59

Merged
SteveSimonson merged 2 commits into
mainfrom
feature/admin-audit-log
Aug 1, 2026
Merged

feat: admin activity log (logins + config changes)#59
SteveSimonson merged 2 commits into
mainfrom
feature/admin-audit-log

Conversation

@SteveSimonson

Copy link
Copy Markdown
Owner

Fixes #58

Summary

Admin Activity log tab records every login (and denial), logout, and config save with actor + IP. Stored in ADMIN_KV key audit_log (last 500 events).

Also answers “who can Google sign in?” by showing the allowlist from ADMIN_ALLOWED_EMAILS on the same tab.

Events

Action When
login Google or password success
login_denied Bad password, non-allowlisted email, unverified email, OAuth failure
logout Explicit logout
config_save PUT config — which sections changed

Test plan

  • build / lint / test
  • Login → Activity log shows login row
  • Save Editor config → config_save with sections
  • Denied Google email → login_denied (if tested)

Append login, login_denied, logout, and config_save events to ADMIN_KV
audit_log (ring buffer). New Activity log tab lists actors, detail, IP.
Also surfaces ADMIN_ALLOWED_EMAILS allowlist for operators. Fixes #58.
@SteveSimonson

Copy link
Copy Markdown
Owner Author

Independent review — admin activity log (#59 / fixes #58)

Reviewed worker/admin.ts, src/pages/Admin.tsx, and AGENTS.md with focus on audit completeness, KV ring-buffer safety, auth on GET /api/admin/audit, and secret leakage.

Audit completeness — pass

Event Coverage
Google login success login after session mint
Google email unverified login_denied
Google not allowlisted login_denied
Google OAuth callback throw login_denied (“callback failed”)
Password success login
Bad password login_denied (no password material logged)
Logout logout with session actor (skipped if no session)
Config PUT config_save with actor + section diff via configChangeSummary

Actor + IP (CF-Connecting-IP / first X-Forwarded-For) are attached consistently. Config audit records section names only (meta.sections), not config payloads — good.

Minor gaps (non-blocking): Google error= cancel and invalid OAuth state paths do not write login_denied; PR copy implies broad “OAuth failure” coverage but only the catch path is instrumented.

Auth on GET /api/admin/auditpass

Endpoint sits after the shared gate:

const session = await readSession(request, env)
if (!session) {
  return json({ ok: false, error: 'Unauthorized' }, 401)
}

Unauthenticated callers get 401. Allowlist is only returned behind that session check.

KV ring buffer — pass (POC-safe)

  • Newest-first prepend + .slice(0, AUDIT_MAX) (500)
  • Malformed KV → []; non-array → []
  • appendAudit failures are logged and swallowed so login/config paths still succeed
  • Value size at 500 short entries is well under KV limits

Nit: classic KV read-modify-write race under concurrent writes can drop events (acceptable for low-traffic admin POC; no locking/list API).

Nit: Number(limit) NaN (e.g. ?limit=foo) yields empty entries because slice(0, NaN)[]. UI always sends 150, so low risk. Prefer const n = Number(...); const limit = Number.isFinite(n) ? clamp(n) : 100.

Nit: response total is page length, not full log length — slightly misleading.

Secret leakage — pass

  • No passwords, session secrets, or OAuth client secrets in audit rows or API body
  • allowedEmails from ADMIN_ALLOWED_EMAILS is intentional and only for authenticated admins (matches product goal “who can Google sign in?”)
  • Denied-login emails are stored as audit signal — appropriate for an admin access log

UI — pass with small UX nits

Activity log tab, refresh, action styling, allowlist panel look correct. Error path in loadAudit writes the failure message into allowlistNote, so load errors appear under “Who can sign in with Google?” rather than as a log error — cosmetic.

Other nits

  1. isAuthed is now unused after switching the auth gate to readSession for actor fields — dead code, can remove or keep for helpers.
  2. No unit tests for appendAudit / ring cap / configChangeSummary (optional for this POC).
  3. Build / existing tests pass locally on this branch.

Summary

Meets the issue: complete auth/config audit trail with actor, KV-capped storage, auth-gated read API, and intentional allowlist display only to authed admins. Remaining items are polish / concurrency / dead code — not merge blockers.

VERDICT: APPROVE_WITH_NITS

@SteveSimonson
SteveSimonson merged commit 6d5229f into main Aug 1, 2026
@SteveSimonson
SteveSimonson deleted the feature/admin-audit-log branch August 1, 2026 23:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Admin activity log: logins + config changes per user

1 participant