Phase 2: M1 closed - re-auth on account delete + storage teardown

↗ view on GitHub · Claude · 2026-05-15 · ed34140e

DELETE /user/account is now properly destructive and properly gated.

Re-authentication:
- Credentials users (password_hash present) must supply `password`
  in the JSON body. Missing returns 401 with code password_required;
  wrong returns 401 with detail "Incorrect password" (timing-safe
  via bcrypt.compare).
- Entra-only users (no password_hash) must supply `confirm_text`
  matching their lowercase email. Missing or wrong returns 401 with
  code email_confirmation_required and the expected email in the
  detail so the UI can echo it back. Defeats casual misuse of a
  still-valid session via browser extension, stolen Bearer, or
  shared device.

Storage teardown:
- Before deleting the row, enumerate every storage_path and
  pdf_storage_path across the user's document_versions (joined to
  documents). Set of unique paths is best-effort deleted after the
  cascade commits. Failures are logged but do not block the user
  delete; orphaned storage objects are easier to detect later than
  missing data is to recover.

Audit:
- audit_events row written inside the same transaction as
  DELETE FROM users so the actor identity survives the cascade
  (audit_events.user_id is ON DELETE SET NULL).
- Metadata captures storage_objects count, had_password,
  had_entra so an admin scrolling the log can see the deletion's
  scope without joining to the now-missing user row.

Frontend:
- Account > Delete Account panel grew password + email-confirm
  inputs. Validates locally that at least one is non-empty before
  enabling the destructive button. On 401 the server's detail is
  surfaced inline (with the expected email for Entra-only users).
- mikeApi.deleteAccount now takes an options object
  { password?, confirmText? }.

End-to-end verified against Postgres 16:
- Credentials: missing pw 401, wrong pw 401, correct pw 204 with
  full cascade (project/document/version all removed) and audit
  row with metadata { storage_objects: 2, had_password: true,
  had_entra: false }.
- Entra-only: missing confirm 401, wrong email 401, correct email
  204. Two audit rows recorded across both deletions.

Closes audit finding M1.
Repository cpatpa/PIP
Author Claude <noreply@anthropic.com>
Authored
Parents e114baf9
Stats 4 files changed , +239 , -33
Part of Phase 2 - Supabase JS → plain pg cutover across the backend

Capture this commit into my fork

Download a Markdown prompt that tells Claude how to port this exact commit into your working tree. Run it via claude -p < capture-commit-ed34140e.md from inside the repo you want the change in.

⬇ Download capture-commit-ed34140e.md