Instant, irreversible account deletion with a single confirmation click is a UX failure that creates real harm — users can accidentally delete years of content, and phishing attacks can trick users into triggering deletion. GDPR Art. 17 does not require immediate deletion; a grace period is both compliant and protective. Without a cooling-off window and email confirmation, there is no mechanism to detect unauthorized deletion requests (e.g., from a stolen session token) before the damage is permanent.
High because irreversible instant deletion with no grace period eliminates the only practical safeguard against accidental or malicious account removal.
Schedule deletion rather than executing it immediately. Create a deletion_requests table, send a confirmation email, and run the actual erasure only after the grace period expires. In src/app/api/account/delete/route.ts:
async function initiateAccountDeletion(userId: string, userEmail: string) {
const scheduledFor = new Date();
scheduledFor.setDate(scheduledFor.getDate() + 30); // 30-day grace
const request = await db.deletionRequest.create({
data: { userId, status: 'pending', scheduledFor, confirmationToken: generateToken() }
});
await sendEmail({
to: userEmail,
subject: 'Confirm account deletion',
body: `Your account is scheduled for deletion on ${scheduledFor.toDateString()}. Cancel here: ${baseUrl}/account/deletion-cancel?token=${request.confirmationToken}`
});
return request;
}
Run a daily cron job that processes deletionRequests where scheduledFor < NOW() and status = 'confirmed', then invokes the full erasure logic.
ID: community-privacy-controls.data-rights.deletion-cooling-off
Severity: high
What to look for: Enumerate every relevant item. Examine account deletion flow. Check whether deletion is immediate or requires a confirmation/cooling-off period. Look for UI that asks for confirmation, sends a confirmation email, or schedules deletion for later. Verify the period is documented (e.g., 30-day grace period).
Pass criteria: At least 1 of the following conditions is met. Account deletion requires explicit confirmation (two-factor) and has a documented grace period (e.g., 7-30 days) during which the account can be restored. After the period, deletion becomes irreversible. User receives email confirmation.
Fail criteria: Deletion is immediate with no cooling-off period. No confirmation mechanism. User cannot cancel deletion after requesting it.
Skip (N/A) when: Never — deletion confirmation is a user protection.
Detail on fail: Describe the deletion flow. Example: "Account deletion is immediate with one-click confirmation. No grace period, no email confirmation, no way to restore." or "Confirmation email sent but deletion executes instantly without waiting for user to confirm."
Remediation: Implement deletion with cooling-off period:
async function initiateAccountDeletion(userId: string) {
const deletionScheduled = new Date();
deletionScheduled.setDate(deletionScheduled.getDate() + 30); // 30-day grace period
const request = await db.deletionRequest.create({
data: {
userId,
status: 'pending',
scheduledFor: deletionScheduled,
confirmationToken: generateToken(),
}
});
// Send confirmation email
await sendEmail({
to: user.email,
subject: 'Confirm account deletion',
body: `Click here to confirm: ${baseUrl}/delete-confirm?token=${request.confirmationToken}`
});
return request;
}
async function confirmAccountDeletion(token: string) {
const request = await db.deletionRequest.findUnique({
where: { confirmationToken: token }
});
if (new Date() < request.scheduledFor) {
return { message: `Deletion scheduled for ${request.scheduledFor}. You can cancel until then.` };
}
// After grace period, execute deletion
await deleteUserPermanently(request.userId);
}