An in-app 'Delete Account' button that calls a stub function or only deletes the auth record — leaving profile tables, user content, and uploaded files intact — creates GDPR Art.17 and CCPA §1798.105 liability while giving users false confidence their data is gone. The more dangerous failure: an unauthenticated deletion endpoint (OWASP A01, CWE-284, CWE-639) allows any caller to delete any user's account by guessing or enumerating user IDs — a complete access-control failure that turns a privacy feature into a weaponisable IDOR vulnerability.
Medium because an incomplete server-side deletion endpoint creates regulatory liability and, if unauthenticated, becomes an IDOR vulnerability enabling account destruction at scale.
Implement cascading deletion: auth record → profile → user content → uploaded files → analytics opt-out. Verify the calling JWT matches the userId being deleted (or require admin role):
// Edge Function: delete-account.ts
// Verify calling user matches target userId
await supabase.from('user_files').delete().eq('user_id', userId);
await supabase.from('posts').delete().eq('user_id', userId);
await supabase.from('profiles').delete().eq('id', userId);
await supabase.auth.admin.signOut(userId, 'global');
await supabase.auth.admin.deleteUser(userId);
Search for the deletion endpoint in src/app/api/ or route handlers and verify it has an authentication check before the deletion logic. A no-op stub or missing auth check both fail this check regardless of whether the in-app UI exists.
app-store-privacy-data.data-handling.server-side-deletion-apimediumDELETE /api/user, DELETE /api/account, DELETE /api/v1/users/:id, /api/account/delete, POST /api/account/close in route handlers (Next.js API routes, Express routes, Supabase Edge Functions, FastAPI routes, etc.). Verify the handler: (a) authenticates the request (checks session/token — a user should only be able to delete their own account); (b) actually deletes or schedules deletion of user data (not just the auth record — also profile data, posts, messages, uploaded files); (c) revokes all sessions after deletion; (d) returns an appropriate success response. Also look for a privacy request handling mechanism (separate from self-service deletion) for GDPR Article 17 requests received by email — a DELETE /api/admin/users/:id/gdpr-delete admin route or similar."DELETE /api/user endpoint found but only calls supabase.auth.admin.deleteUser() — profile table, user_posts, and uploaded files are not deleted" or "Account deletion endpoint found but has no authentication check — any caller can delete any user's account (IDOR)"// Edge Function: delete-account.ts
await supabase.from('user_files').delete().eq('user_id', userId);
await supabase.from('posts').delete().eq('user_id', userId);
await supabase.from('profiles').delete().eq('id', userId);
await supabase.auth.admin.deleteUser(userId);
// storage.remove() for uploaded files
supabase.auth.admin.signOut(userId, 'global') before deleting the user