Skip to content

feat(security): #23 user-facing security-activity dashboard#131

Merged
TortoiseWolfe merged 1 commit into
mainfrom
feat/user-audit-trail-23
Jun 5, 2026
Merged

feat(security): #23 user-facing security-activity dashboard#131
TortoiseWolfe merged 1 commit into
mainfrom
feat/user-audit-trail-23

Conversation

@TortoiseWolfe

Copy link
Copy Markdown
Owner

Closes #23 (005 Security Hardening — the last open gap).

Context

#23 had 3 tasks; two were already shipped — session-timeout (IdleTimeoutModal + useIdleTimeout, integrated into AuthContext) and pre-commit secret detection (gitleaks + .gitleaks.toml in .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.tsxProtectedRoute-wrapped route (mirrors /account/page.tsx).
  • /account now links to it ("View recent security activity").

Security

Isolation is enforced by RLS, not client code: the auth_audit_logs policy "Users can view own audit logs" (auth.uid() = user_id) means a plain authenticated SELECT returns only the caller's own rows. The component does not add a client-side user_id filter for isolation (would be a false safeguard).

Verification

  • ✅ 10/10 tests (7 unit + 3 accessibility, 0 axe violations)
  • ✅ type-check clean, lint clean
  • ✅ component-structure validator 107/107 (5-file pattern)

Note found in passing (out of scope)

The generated src/lib/supabase/types.ts is staleauth_audit_logs.Row omits the success / error_message columns that exist in the live table + migration (it mis-inferred a SelectQueryError). 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

…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>
@TortoiseWolfe TortoiseWolfe merged commit 757825d into main Jun 5, 2026
17 checks passed
@TortoiseWolfe TortoiseWolfe deleted the feat/user-audit-trail-23 branch June 5, 2026 21:58
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>
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.

[Gap-Audit] 005 Security Hardening: complete session-timeout UI + audit dashboard + secret detection

2 participants