feat(security): #23 user-facing security-activity dashboard#131
Merged
Conversation
…tTrail + /account/audit) Completes the last gap of #23 (005 Security Hardening). Tasks A (session- timeout IdleTimeoutModal) and B (gitleaks pre-commit) were already shipped; this adds the user-facing audit dashboard: - src/components/molecular/UserAuditTrail/ (5-file pattern) — renders the signed-in user's own auth audit log (sign-ins, password changes, verification, etc.). Security is RLS: the 'Users can view own audit logs' policy (auth.uid() = user_id) means a plain authenticated SELECT returns only the caller's rows — no client-side user_id filter is trusted for isolation. Humanized event labels, success/failed badges, loading/empty/error states. - src/app/account/audit/page.tsx — ProtectedRoute-wrapped route, mirrors /account/page.tsx. - /account links to it ('View recent security activity') for discoverability. Verified: 10/10 tests (7 unit + 3 a11y, 0 axe violations), type-check clean, lint clean, component-structure validator 107/107. Note found in passing (out of scope, left a code comment): the generated src/lib/supabase/types.ts is stale — auth_audit_logs.Row omits the success / error_message columns that exist in the live table + migration. Worked around with a boundary cast; regenerating the Supabase types is a separate task. Closes #23. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This was referenced Jun 5, 2026
TortoiseWolfe
added a commit
that referenced
this pull request
Jun 6, 2026
…nt-retry schema drift (#135) Regenerated src/lib/supabase/types.ts from the live schema via the Supabase Management API (GET /v1/projects/{ref}/types/typescript — no local CLI install). The old generated types were stale and were MASKING real bugs: 1. auth_audit_logs.Row was missing success/error_message → removed the boundary-cast workaround added in #131 (UserAuditTrail.tsx). 2. The fresh types exposed that payment_intents.retry_count + parent_intent_id DON'T EXIST in the live DB — yet payment-service.ts (FR-009 retry limit) and usePaymentRetryStatus.ts read/write them. retryPayment() would fail at runtime; only the mocked unit tests passed. The columns ARE in the monolithic migration (lines 69-76, #43/B1) — they were just never applied to prod (same prod-vs-migration drift class as #49). Applied the idempotent ADD COLUMN IF NOT EXISTS to prod via the Management API + verified; the payment-retry feature now actually works. 3. My #26 recordSystemMessage insert was missing the required messages. sequence_number — the fresh (stricter) types caught it; added the placeholder :0 (the assign_sequence_number() trigger overwrites it, matching message-service.ts:335/516). Also picks up edge_idempotency_keys (the #130 table) now that it's in the types. Gates: type-check clean (was failing on retry_count before the prod fix), lint clean, 41 affected tests pass (UserAuditTrail / payment retry / group membership). Co-authored-by: TurtleWolfe <TurtleWolfe@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #23 (005 Security Hardening — the last open gap).
Context
#23 had 3 tasks; two were already shipped — session-timeout (
IdleTimeoutModal+useIdleTimeout, integrated intoAuthContext) and pre-commit secret detection (gitleaks +.gitleaks.tomlin.husky/pre-commit). The remaining gap was the user-facing security-activity dashboard (the admin one,AdminAuditTrail, already existed).What
src/components/molecular/UserAuditTrail/(5-file pattern) — renders the signed-in user's own auth audit log: sign-ins, password changes, verification, OAuth link/unlink, etc. Humanized event labels, success/failed badges, IP, timestamps; loading / empty / error states.src/app/account/audit/page.tsx—ProtectedRoute-wrapped route (mirrors/account/page.tsx)./accountnow links to it ("View recent security activity").Security
Isolation is enforced by RLS, not client code: the
auth_audit_logspolicy "Users can view own audit logs" (auth.uid() = user_id) means a plain authenticatedSELECTreturns only the caller's own rows. The component does not add a client-sideuser_idfilter for isolation (would be a false safeguard).Verification
Note found in passing (out of scope)
The generated
src/lib/supabase/types.tsis stale —auth_audit_logs.Rowomits thesuccess/error_messagecolumns that exist in the live table + migration (it mis-inferred aSelectQueryError). Worked around with a documented boundary cast; regenerating the Supabase types is a separate task worth filing.Part of the #115 backlog sweep (Tier-1 deferred Gap-Audit). 🤖 Generated with Claude Code