Skip to content

Add HTTP endpoint + UI for end users to delete their own account #352

Description

@melvincarvalho

Summary

A logged-in user has no way to delete their own account through the running JSS. The data-layer function (deleteAccount(id) in src/idp/accounts.js) and the CLI (jss account delete <user>, #292) both exist, but neither is reachable by the account holder over HTTP, and there's no confirmation UI for what is — by design — a destructive action.

This is the deletion analogue of #351 (self-service password change). Same auth shape, plus a confirmation step because the action is irreversible.

Current state

Proposed shape

Endpoint

DELETE /idp/account
Authorization: <existing session / DPoP>
Content-Type: application/json

{
  "currentPassword": "...",
  "purgeData": false
}
  • Owner-only — caller's WebID must match the target account's WebID.
  • currentPassword is required as proof (matches the password-change pattern in Add HTTP endpoint for end users to change their own password #351).
  • purgeData: true mirrors the CLI's --purge — also removes the pod's filesystem data after revoking the account.
  • Response: 204 on success, all sessions for the rotated account invalidated, the bearer token used for this request is the last valid one and dies with the account record.
  • 401 wrong password / unauthenticated; 403 cross-account; 404 already gone.

UI: /idp/account/delete (or /idp/settings#delete)

Authenticated HTML form, owner-only, with explicit warning copy:

  • "This permanently deletes account <username> and revokes all sessions and tokens. It cannot be undone."
  • Checkbox: "Also delete my pod data (<root>/<podName>/...) — this destroys all my files on this server." Off by default.
  • Required field: re-enter current password.
  • Optional field: type the username/WebID as a second confirmation (typical destructive-action ergonomic pattern).

Server-side, the form posts to the DELETE /idp/account endpoint above (HTML form → JS → DELETE, or a POST /idp/account/delete shim that internally maps to the DELETE handler).

Out of scope

  • Self-service operator → admin elevation (operator deleting other users' accounts via HTTP). Stays CLI-only for now; not the same risk profile.
  • Single-user mode self-deletion: in single-user mode there's exactly one account, deleting it bricks the server until restart re-seeds. We probably want to either disable the endpoint in single-user mode, or surface an extra warning about the consequences. Decide as part of implementation; the safe default is to disable.
  • Federation / Solid-OIDC ripple effects — type indexes, ActivityPub follows, nostr relays etc. that reference the dead WebID. The deletion endpoint can't reach those; document that the URI becomes a tombstone.

Acceptance

  • New endpoint accepts a valid {currentPassword} from the authenticated owner and removes the account record + indexes; returns 204.
  • purgeData: true additionally removes the pod's filesystem data.
  • Wrong currentPassword → 401, account untouched.
  • Cross-account delete attempts → 403, account untouched.
  • All other active sessions / refresh tokens for the deleted account are invalidated.
  • New /idp/account/delete (or equivalent) HTML page, behind auth, with the warning copy and re-entry of password.
  • In single-user mode, the endpoint either 403s with a clear message or follows whatever single-user policy gets agreed in implementation.
  • Tests cover happy path + the failure modes above + the data-purge variant.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions