Skip to content

feat(webapp): admin can delete users from /admin#3463

Open
isshaddad wants to merge 9 commits intomainfrom
feat/admin-account-deletion
Open

feat(webapp): admin can delete users from /admin#3463
isshaddad wants to merge 9 commits intomainfrom
feat/admin-account-deletion

Conversation

@isshaddad
Copy link
Copy Markdown
Collaborator

@isshaddad isshaddad commented Apr 28, 2026

CI typecheck is expected to fail until the cloud PR ships. This PR consumes BillingClient.deleteUser and DeleteUserResponse, both of which are added in the pair PR's release of @trigger.dev/platform@1.0.28. The webapp is currently pinned to 1.0.27 (the latest published version), which doesn't yet have those exports.

Summary

  • Adds a Delete button to the admin users table at /admin, with a
    type-to-confirm modal and a server-side guard against self-delete.
    The action proxies to the platform service, which performs the
    deletion server-side.
  • Schema migration: PersonalAccessToken.userId and MfaBackupCode.userId
    switch from default Restrict to onDelete: Cascade so user deletion
    succeeds at the FK level. All other User relations were already correct.
  • New UserDeletionAuditLog table records each admin deletion
    (denormalized, no FKs to User so the row survives).

Test plan

  • Migration applies cleanly (pnpm run db:migrate)
  • /admin shows Delete next to Impersonate; the button does not
    appear on the signed-in admin's own row
  • Confirm modal requires typing delete <email> exactly to enable
  • POST /admin with intent=delete + your own user id returns 400
  • Successful delete redirects to /admin?deleted=1 with a success
    banner; an audit row is written to UserDeletionAuditLog
  • Re-deleting an already-gone user returns the success banner
    (idempotent)

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Apr 28, 2026

⚠️ No Changeset found

Latest commit: 8a11f9e

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 28, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds an admin user-deletion flow: documentation for a server-side delete endpoint; a new DeleteUserDialog React component requiring the exact delete {email} confirmation and posting intent=delete; admin index loader/action updated to handle intent=delete (authorization, self-delete prevention, client IP extraction, call to platform deletion service, redirect on success, typed JSON errors) while preserving impersonation under intent=impersonate; new platform deleteUser service wrapper; Prisma migration and UserDeletionAuditLog model; MfaBackupCode/PersonalAccessToken relations set to cascade; and a change to PAT auth to best-effort lastAccessedAt updates with error handling.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Additional context

  1. Admin route: schema-validated form intents (delete and impersonate), typed JSON error responses for failures/unknown intents, redirects on successful delete via /admin?deleted=1.
  2. UI: modal form includes hidden intent=delete and id, disables submit until typed confirmation matches delete {email}, closes on navigation/submission.
  3. Platform service: deleteUser(userId, { adminUserId, adminEmail, reason?, ipAddress? }) added; errors are logged/thrown.
  4. Database: migration adds UserDeletionAuditLog table, indexes, and adjusts FK constraints; Prisma schema adds UserDeletionAuditLog model and sets onDelete/onUpdate: Cascade for MfaBackupCode.user and PersonalAccessToken.user.
  5. Auth: authenticatePersonalAccessToken uses updateMany to touch lastAccessedAt, logs and treats a zero-count update as a miss.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main feature: enabling admin users to delete users from the /admin interface.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description check ✅ Passed The PR description provides comprehensive context, test plan, and implementation details despite noting CI typecheck will fail until a paired cloud PR ships.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/admin-account-deletion

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

coderabbitai[bot]

This comment was marked as resolved.

@isshaddad isshaddad marked this pull request as ready for review April 28, 2026 16:56
devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 1 new potential issue.

View 7 additional findings in Devin Review.

Open in Devin Review

Comment on lines +69 to +76
const targetId = deleteAttempt.data.id;

if (targetId === admin.id) {
return typedjson(
{ error: "You can't delete your own account from the admin UI." },
{ status: 400 }
);
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Missing server-side guard prevents deletion of other admin users

The UI hides the Delete button for admin users via !user.admin at apps/webapp/app/routes/admin._index.tsx:229, but the server-side action handler only prevents self-deletion (targetId === admin.id at line 71). There is no server-side check verifying the target user is not an admin. A crafted POST request with intent=delete&id=<other-admin-id> bypasses the UI restriction and deletes another admin account. Authorization checks must be enforced server-side to be effective.

Prompt for agents
In the delete action handler in apps/webapp/app/routes/admin._index.tsx, after the self-deletion check (targetId === admin.id), add a server-side check that loads the target user from the database and verifies they are not an admin before proceeding with deletion. This mirrors the client-side guard at line 229 that hides the Delete button when user.admin is true. Without this, a crafted POST request can delete any admin user. You could use prisma to find the target user and check their admin flag, returning a 403/400 error if the target is an admin.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

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.

1 participant