5.4 KiB
Account deletion: dedicated email proof
The chosen flow requests a dedicated email, verifies either its code or its link, then requires a separate explicit final deletion confirmation. The email proof never logs in and never deletes data.
Implemented in this step: AccountDeletionProofs issues a separate challenge for an active private account with a valid device login and verified email identity. Guest access is excluded. The public deletion page and authenticated POST endpoints now connect issuance, verification and the final transaction. The Settings account section links to this flow for private accounts; guest accounts cannot use it.
Email codes and links (both login and deletion) now last two hours from issuance to allow for greylisting delays. RFC 5321 section 4.5.4.1 recommends retry intervals of at least 30 minutes; this is a practical allowance, not a guarantee for arbitrary mail delays. Previously issued tokens retain their original expiration. Five failed attempts disable both alternatives, one-minute resend cooldown and existing persistent mail request limits remain in effect. A new request replaces the previous challenge.
Deletion verification consumes both alternatives atomically and produces a separate random grant bound to the confirming account and device. The grant lasts five minutes, capped at the email challenge's expiration. Logout, account deactivation, expiry and reissuance prevent its use. Grant consumption must run inside the final deletion transaction so a failed deletion rolls back consumption as well.
The additive Liquibase changeset creates only the deletion-proof table and keeps existing account, device, customer, project, work and habit data. Tests upgrade a filled H2 file database and compare existing rows before and after migration. No applied changeset is modified.
Sources: https://www.rfc-editor.org/rfc/rfc5321.html#section-4.5.4.1 and https://www.rfc-editor.org/rfc/rfc6647.html .
Validation completed: seven deletion-proof tests passed, including an H2 file migration preserving existing rows, concurrent redemption, delayed delivery, expiration, device revocation and rollback. Full local CI passed: 120 JVM tests executed, one separate browser harness skipped, 22 operations tests, frontend tests and production smoke at version 0.1.6-SNAPSHOT. No real email was sent.
Atomic removal
AccountDeletion.delete requires explicit confirmation and consumes the fresh device-bound grant inside the deletion transaction. It removes the authenticated owner's work and habit records, mutation receipts, default markers, settings, identity mappings, pending login challenge and device credentials. Device cascades remove push subscriptions, reminder deliveries and the deletion challenge. Foreign accounts remain untouched; no target account ID is accepted from callers.
Issued invitation consumption markers remain present. Outstanding invitations issued by the deleting account are revoked and their creator references become null. A deleted email can register again only through a new invitation, obtaining a new ID without inherited data or administrator rights. Deleting the last administrator keeps bootstrap consumed and clears any old recovery authorization. Operator recovery requires the explicit server command documented in admin-recovery.md.
Any database failure rolls back the entire removal and grant consumption. Automated deletion tests use isolated databases. The connected UI requires the email proof and a separate required confirmation checkbox; only the final protected POST invokes this transaction.
Atomic-removal validation: five AccountDeletionTest cases passed (complete removal and foreign-data retention, confirmation/device isolation, late constraint rollback, new invitation/new empty account, concurrent confirmation). Final run also included all seven proof tests and both email-login HTTP tests: 14 passed at version 0.1.8-SNAPSHOT.
Browser and HTTP flow
GET /account/delete never consumes a proof or deletes data. It shows sign-in instructions for anonymous and guest visitors. A signed-in private account can request the dedicated email or enter its code. Fragment links are removed from browser history and submitted only to the verification endpoint. If the device is signed out, sign in first and reopen the link or enter its code; deletion tokens are never sent to the login verifier.
The raw final grant lives in the HTTP session, not in HTML or a URL. Cancel clears it. Every mutation requires the authenticated account and CSRF protection. A stale CSRF form shows a recovery link and states that nothing was deleted. Successful deletion invalidates the session, clears the device cookie and renders a success page. Its script clears only that account's local drafts and broadcasts the existing logout event to other tabs. Device preferences remain intact.
UI validation: full local CI passed with 128 JVM tests executed (one separate browser harness skipped), 22 operations tests, frontend tests and production smoke. HTTP tests cover email request, code/link verification without deletion, final confirmation, CSRF, anonymous/guest protection, cancellation, and session/cookie invalidation. Local browser acceptance used a temporary database and fake sendmail, verified the Settings entry, German confirmation flow at 360px, required checkbox and cancellation. No external email or production deletion occurred; browser console was clean. Version 0.1.10-SNAPSHOT.