Users can delete their account and PII is actually purged
Why it matters
GDPR Art. 17 ("right to be forgotten") and CCPA §1798.105 both require that a user who asks for their account and personal data to be deleted actually gets it deleted — not hidden behind a deletedAt timestamp while the email, name, and history sit indefinitely in the database. CNIL fined Clearview AI €20M in 2022 in part for failing to act on erasure requests, and ICO enforcement routinely issues multi-million-pound penalties on the same grounds. Beyond regulatory exposure, the Apple App Store and Google Play Store have both mandated in-app account deletion since 2022: apps that gate deletion behind a support-email workflow get rejected at review. AI coding tools reliably scaffold signup, login, and password reset, then skip the deletion route entirely — because "how do I build auth" is a well-represented training pattern and "how do I build GDPR-compliant erasure" is not. The quiet failure mode is a live app that passes every happy-path test while accumulating unlawful retention exposure with every new user.
Severity rationale
Critical because missing or stub-only deletion is a direct ongoing Art. 17 violation accruing exposure per active user per day, blocks App Store and Play Store approval outright, and cannot be patched retroactively against users who already asked and were ignored.
Remediation
Ship an authenticated DELETE route (e.g. app/api/account/route.ts) that either hard-deletes the user row with cascades to related PII-bearing tables, or anonymizes in place (email = null, name = 'Deleted user', pii_purged = true). If you soft-delete first, add a scheduled purge job that runs after a documented 14–30 day grace period and removes the underlying data across every table, object-storage bucket, and third-party processor that holds it.
Deeper remediation guidance and cross-reference coverage for this check lives in the gdpr-readiness and ccpa-readiness Pro audits — run those after applying this fix for a more exhaustive pass on the same topic.
Detection
- ID:
account-deletion-coded - Severity:
critical - What to look for: First determine whether accounts exist — auth library in
package.json(next-auth,@clerk/*,@supabase/supabase-js,lucia,auth0,firebase/auth,better-auth), OR auth routes (/auth/*,/signup,/login), ORUser/Accountmodel inprisma/schema.prismaoruserstable in migrations. If no accounts, skip. If accounts exist, enumerate: (a) a DELETE handler atapp/api/user/*,app/api/account/*,app/api/me/*,pages/api/user/*, or a server action matching/^(delete|anonymize|purge)(Account|User|Me)$/i; AND (b) the handler must actually remove or anonymize PII —prisma.user.delete(...),supabase.auth.admin.deleteUser(...),DELETE FROM users, orUPDATE users SET email = null / pii_purged = true / name = 'Deleted'. - Pass criteria: Accounts exist, authenticated DELETE endpoint or server action exists, and the body performs real deletion / anonymization / soft-delete-plus-purge-cron.
- Fail criteria: Accounts exist and either no deletion endpoint OR the endpoint only flips
deletedAt/isDeletedwith no paired purge job (no route underapp/api/cron/*orsupabase/functions/*that purges past-grace-period rows). Soft-delete without a scheduled follow-up is not erasure. - Skip (N/A) when: No accounts. Quote:
"No auth library, auth routes, or user model — project has no user accounts". - Before evaluating, quote: Deletion handler file path + 1–3 line excerpt showing the operation. If no handler, quote the auth evidence proving accounts exist.
- Report even on pass:
"Deletion handler at <path>; mechanism: hard-delete | anonymize | soft-delete+cron at <cron-path>". - Detail on fail:
"next-auth present, /login route exists, but no DELETE endpoint under app/api/user/, account/, or me/"or"app/api/account/route.ts only runs prisma.user.update({ data: { deletedAt: new Date() } }) — no purge cron". - Remediation: Ship an authenticated DELETE at
app/api/account/route.tsthat hard-deletes (with cascades), anonymizes-in-place (email = null,name = 'Deleted user',pii_purged = true), OR soft-deletes paired with a scheduled purge after a 14–30 day grace period across every table, storage bucket, and third-party processor. Deeper coverage:gdpr-readiness,ccpa-readiness.
External references
- gdpr:eu-2016-679 · Art. 17 — Right to erasure
- ccpa:cal-civ-1798 · §1798.105 — Right to delete
Taxons
History
- 2026-04-23·v1.0.0·Initial Phase 9.1 v3.1 Stack Scan promotion — coded account deletion with real PII purge is a direct GDPR Art. 17 + CCPA §1798.105 obligation plus an App Store / Play Store gate.·by phase-9-1-stack-scan-v3-1