Summary
The admin dashboard's get_security_metrics() function reports signups_this_month as the count of auth_audit_logs rows where event_type = 'sign_up' (monolithic migration line 752). The metric reads from a table that not every signup path writes to, so the dashboard underreports.
Surfaced while verifying #44 (commit 6533567): a test asserted ≥2 audit_log rows from "signup events from user creation" and failed because admin-API createUser() produced zero such rows. Fixing that test was correct, but the underlying instrumentation gap is real.
What's shipped
src/lib/auth/audit-logger.ts — logAuthEvent() library function
src/services/auth/audit-logger.ts — AuditLogger.logSignUp(), logSignIn() service class
- Email/password
SignUpForm.tsx:11 calls logAuthEvent on successful submit
auth_audit_logs table + RLS policies + get_security_metrics() consumer (line 748)
- Existing
on_auth_user_created AFTER INSERT ON auth.users trigger calls create_user_profile() (line 425)
Gap
event_type = 'sign_up' rows are not written for:
- OAuth signups —
auth.signInWithOAuth() redirects don't go through SignUpForm, so logAuthEvent is never called.
- Admin-API user creation —
supabase.auth.admin.createUser() bypasses the form path entirely. Used by tests, scripts, and any future admin tooling.
- Race window — even on the email/password path,
logAuthEvent runs as the freshly-authenticated session, so it can race with token establishment on slow networks.
Net effect: signups_this_month is a lower bound, not a count.
Plan
Trigger-based, single source of truth:
- Extend the existing
create_user_profile() function in the monolithic migration to also INSERT INTO auth_audit_logs (user_id, event_type, success, event_data) with event_type = 'sign_up'. The function already runs AFTER INSERT ON auth.users for every signup path (form, OAuth, admin API).
- Decide whether to keep the application-level
logAuthEvent calls in SignUpForm.tsx. Likely remove them to avoid double-writes — the trigger now owns this event.
- Re-evaluate the test that prompted this work (
tests/rls/service-role.test.ts "service role can read all audit logs"). Once the trigger lands, restoring ≥2 makes sense (one from each createTestUser call in beforeAll).
- Audit the other
event_type values queried by get_security_metrics() (sign_in, sign_in_success, sign_in_failed) for similar gaps. sign_in doesn't have an auth.users INSERT to hook — that one stays in application code.
Reference
Summary
The admin dashboard's
get_security_metrics()function reportssignups_this_monthas the count ofauth_audit_logsrows whereevent_type = 'sign_up'(monolithic migration line 752). The metric reads from a table that not every signup path writes to, so the dashboard underreports.Surfaced while verifying #44 (commit 6533567): a test asserted
≥2 audit_log rows from "signup events from user creation"and failed because admin-APIcreateUser()produced zero such rows. Fixing that test was correct, but the underlying instrumentation gap is real.What's shipped
src/lib/auth/audit-logger.ts—logAuthEvent()library functionsrc/services/auth/audit-logger.ts—AuditLogger.logSignUp(),logSignIn()service classSignUpForm.tsx:11callslogAuthEventon successful submitauth_audit_logstable + RLS policies +get_security_metrics()consumer (line 748)on_auth_user_created AFTER INSERT ON auth.userstrigger callscreate_user_profile()(line 425)Gap
event_type = 'sign_up'rows are not written for:auth.signInWithOAuth()redirects don't go throughSignUpForm, sologAuthEventis never called.supabase.auth.admin.createUser()bypasses the form path entirely. Used by tests, scripts, and any future admin tooling.logAuthEventruns as the freshly-authenticated session, so it can race with token establishment on slow networks.Net effect:
signups_this_monthis a lower bound, not a count.Plan
Trigger-based, single source of truth:
create_user_profile()function in the monolithic migration to alsoINSERT INTO auth_audit_logs (user_id, event_type, success, event_data)withevent_type = 'sign_up'. The function already runsAFTER INSERT ON auth.usersfor every signup path (form, OAuth, admin API).logAuthEventcalls inSignUpForm.tsx. Likely remove them to avoid double-writes — the trigger now owns this event.tests/rls/service-role.test.ts"service role can read all audit logs"). Once the trigger lands, restoring≥2makes sense (one from eachcreateTestUsercall inbeforeAll).event_typevalues queried byget_security_metrics()(sign_in,sign_in_success,sign_in_failed) for similar gaps.sign_indoesn't have anauth.usersINSERT to hook — that one stays in application code.Reference
044/payment-rls-verify(the test fix that surfaced this)supabase/migrations/20251006_complete_monolithic_setup.sql(monolithic per CLAUDE.md)