Follow-up from #391 (closed #352).
The DELETE /idp/account endpoint shipped, but there's no human-friendly UI to drive it — operators today have to do the two-curl dance:
TOKEN=$(curl -sX POST .../idp/credentials -d '{"email":"…","password":"…"}' | jq -r .access_token)
curl -sX DELETE .../idp/account -H "Authorization: Bearer $TOKEN" \
-d '{"currentPassword":"…","purgeData":true}'
For end users (especially anyone testing key-add flows in #386 Phase B who needs a way to recreate from a borked profile), this needs an HTML form.
Proposed shape
/idp/account/delete — public HTML page (matches the existing unauthenticated /idp landing and /idp/register pages). Asks for credentials at submission time rather than relying on session state.
Form fields
- Username or email — identifies the account
- Current password — re-entry as proof of possession
- Type your username to confirm — destructive-action UX guard (typical pattern)
- ☐ Also delete my pod data — checkbox; off by default. Maps to
purgeData: true.
- Submit — submit triggers the destructive action
Warning copy
"This permanently deletes account <username> and revokes all sessions and tokens. It cannot be undone. Pod data is preserved unless you tick the box below."
Two implementation options
A — server-side login+delete shim (recommended)
New handler that accepts POST /idp/account/delete form-encoded with username/password/purgeData. Internally: validate credentials, call deleteAccount if match, return success/failure HTML. No JS required, accessibility-first.
POST /idp/account/delete (application/x-www-form-urlencoded)
username=...¤tPassword=...&purgeData=on&confirm=...
Pros: works without JavaScript, no client-side token juggling, matches the existing form-submit pattern of /idp/register. Single round-trip.
Cons: blurs the "two-step: identify, then act" model the DELETE endpoint enforces. But functionally identical — user is proving credentials at delete time.
B — client-side JS
Form's submit handler does fetch /idp/credentials (login → token) then fetch DELETE /idp/account (delete using token). Calls the existing endpoints directly, no new server-side handler.
Pros: re-uses the existing endpoint contract verbatim; nothing new server-side.
Cons: requires JS (no graceful degrade); two round-trips; password is in JS scope briefly.
Single-user mode
Same 403 as the endpoint — operator path stays the CLI. Form should detect this server-side and render "Account deletion via HTTP is disabled in single-user mode" with a link to docs explaining the rationale.
Acceptance
Refs
Follow-up from #391 (closed #352).
The
DELETE /idp/accountendpoint shipped, but there's no human-friendly UI to drive it — operators today have to do the two-curl dance:For end users (especially anyone testing key-add flows in #386 Phase B who needs a way to recreate from a borked profile), this needs an HTML form.
Proposed shape
/idp/account/delete— public HTML page (matches the existing unauthenticated/idplanding and/idp/registerpages). Asks for credentials at submission time rather than relying on session state.Form fields
purgeData: true.Warning copy
Two implementation options
A — server-side login+delete shim (recommended)
New handler that accepts
POST /idp/account/deleteform-encoded withusername/password/purgeData. Internally: validate credentials, calldeleteAccountif match, return success/failure HTML. No JS required, accessibility-first.Pros: works without JavaScript, no client-side token juggling, matches the existing form-submit pattern of
/idp/register. Single round-trip.Cons: blurs the "two-step: identify, then act" model the DELETE endpoint enforces. But functionally identical — user is proving credentials at delete time.
B — client-side JS
Form's submit handler does
fetch /idp/credentials(login → token) thenfetch DELETE /idp/account(delete using token). Calls the existing endpoints directly, no new server-side handler.Pros: re-uses the existing endpoint contract verbatim; nothing new server-side.
Cons: requires JS (no graceful degrade); two round-trips; password is in JS scope briefly.
Single-user mode
Same 403 as the endpoint — operator path stays the CLI. Form should detect this server-side and render "Account deletion via HTTP is disabled in single-user mode" with a link to docs explaining the rationale.
Acceptance
/idp/account/deletereturns 200 HTMLpurgeData=oncheckbox produces the same on-disk effect as the JSONpurgeData: true(usesaccount.podName, not the lowercased username — see idp: self-service DELETE /idp/account endpoint (#352) #391 pass 2)test/idp-delete-account.test.js)Refs
src/idp/views.js— analogous form templates (registerPage, etc.)src/idp/credentials.js:handleDeleteAccount— the underlying handler