Skip to content

feat: add self-service account deletion to settings page#4650

Open
GMetaxakis wants to merge 9 commits intoONEARMY:masterfrom
GMetaxakis:feat/delete-account-button
Open

feat: add self-service account deletion to settings page#4650
GMetaxakis wants to merge 9 commits intoONEARMY:masterfrom
GMetaxakis:feat/delete-account-button

Conversation

@GMetaxakis
Copy link
Copy Markdown
Contributor

PR Checklist

  • - Unit and/or e2e tests for the changes that have been added (for bug fixes / features)

PR Type

  • Feature

What is the new behavior?

Adds a "Delete account" section to Settings > Account, replacing the old Discord link. Users can now delete their own account by entering their password and confirming via a modal.

  • Accordion-based form (collapsed by default) with password confirmation
  • Destructive (red) button + confirmation modal with clear warning
  • Server-side API route verifies password, cleans up blocking FK references (notifications, notification preferences), then deletes the auth user via admin API — cascade constraints handle the rest
  • Session is cleared and user is redirected to /

Does this PR introduce a DB Schema Change or Migration?

  • No

Git Issues

Closes #4163

@cypress
Copy link
Copy Markdown

cypress bot commented Mar 3, 2026

onearmy-community-platform    Run #9048

Run Properties:  status check failed Failed #9048  •  git commit 44838b3ab5: Merge branch 'master' into feat/delete-account-button
Project onearmy-community-platform
Branch Review pull/4650
Run status status check failed Failed #9048
Run duration 29m 33s
Commit git commit 44838b3ab5: Merge branch 'master' into feat/delete-account-button
Committer Mário Nunes
View all properties for this run ↗︎

Test results
Tests that failed  Failures 1
Tests that were flaky  Flaky 0
Tests that did not run due to a developer annotating a test with .skip  Pending 0
Tests that did not run due to a failure in a mocha hook  Skipped 0
Tests that passed  Passing 94
View all changes introduced in this branch ↗︎

Tests for review

Failed  src/integration/SignUp.spec.ts • 1 failed test • ci-chrome

View Output Video

Test Artifacts
[User sign-up] > [Update existing auth details] > Updates email, password, and deletes account Test Replay Screenshots Video

@GMetaxakis GMetaxakis marked this pull request as ready for review March 5, 2026 16:52
@GMetaxakis GMetaxakis requested a review from a team as a code owner March 5, 2026 16:52
});

describe('[Delete account]', () => {
it('Rejects wrong password', () => {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Not sure this needs a seperate test. Add as a step to the main test.

cy.get('[data-cy="Confirm.modal: Confirm"]').click();

cy.step('Redirected to homepage');
cy.url().should('eq', Cypress.config().baseUrl + '/');
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Would be good to see what happens to the content of a deleted user:

  • Comments (de-author)
  • Questions (de-author)
  • Research (de-author)
  • Projects (de-author)
  • News (de-author)
  • Map pins (delete)

Copy link
Copy Markdown
Contributor

@mariojsnunes mariojsnunes Mar 24, 2026

Choose a reason for hiding this comment

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

To do that, the best way might be with a seed. Have the profile and associated items which we delete on the test, instead of signing up a new user and creating the content each time.
If that's difficult for some reason, at least this PR must ensure those delete/set null cascades are correct.

const data = new FormData();
data.append('password', password);

return await fetch('/api/account/delete', {
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.

just to double-check, using a POST instead of DELETE because we need to send FormData?

const authId = claims.data?.claims?.sub;

if (!authId) {
return Response.json({ error: 'Unauthorized' }, { headers, status: 401 });
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.

please update the error handling to use the new methods validationError, unauthorizedError etc.
Also need to add a try-catch. see other api endpoints like api.questions.$id.ts


const adminClient = createSupabaseAdminServerClient();

await adminClient.from('notifications').delete().eq('owned_by_id', profile.id);
Copy link
Copy Markdown
Contributor

@mariojsnunes mariojsnunes Mar 24, 2026

Choose a reason for hiding this comment

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

I'd rather use a delete cascade for all of these, so we need a schema change.

await adminClient.from('notifications').delete().eq('triggered_by_id', profile.id);
await adminClient.from('notifications_preferences').delete().eq('user_id', profile.id);

const { error } = await adminClient.auth.admin.deleteUser(authId);
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.

We can't delete the user without checking if the user has a profile on other tenants.

1 user account can have multiple profiles (1 per tenant)

If the user does not have other profiles, we can delete the account, this will cascade delete the profile.
If the user has other profiles, just delete the profile for this tenant (try using the normal client, not the admin client as it is restricted to the current tenant).

Copy link
Copy Markdown
Contributor

@mariojsnunes mariojsnunes left a comment

Choose a reason for hiding this comment

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

Great first step! Still a few things to be improved before merging :)

GMetaxakis and others added 2 commits March 24, 2026 12:09
- Add migration to fix cascade deletes on notifications.triggered_by_id
  (SET NULL) and notifications_preferences.user_id (CASCADE), removing
  the need for manual deletes before profile removal
- Rewrite API route with try-catch, consistent error response pattern
  using statusText, and multi-tenant check (only delete auth user if no
  profiles exist on other tenants, otherwise delete current profile only)
- Update frontend to read errors from statusText
- Merge delete account E2E test into the main auth details test flow
@benfurber benfurber moved this to In progress in Core Team - BAU list Mar 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: No status
Status: In progress

Development

Successfully merging this pull request may close these issues.

[feature] Add Profile delete button

3 participants