Account Deletion Policies

Customize account deletion authorization rules.

Account deletion policies control when users can delete their accounts. The system ships with default policies that can be extended or replaced.

Default Policies

The @kit/account-hooks package includes two default policies:

PolicyIDBehavior
checkSubscriptionsPolicyaccount-deletion.check-subscriptionsBlocks if user has active subscriptions
checkOrganizationsPolicyaccount-deletion.check-organizationsBlocks if sole owner of orgs with other members

Adding Custom Policies

1. Create Your Policy

// In your app or a custom package
import { definePolicy, allow, deny } from '@kit/policies';
import type { AccountDeleteContext } from '@kit/account-hooks';
export const gracePeriodPolicy = definePolicy<AccountDeleteContext>({
id: 'account-deletion.grace-period',
evaluate: async (context) => {
const user = await getUser(context.userId);
const daysSinceCreation = daysBetween(user.createdAt, new Date());
if (daysSinceCreation < 7) {
return deny({
code: 'ACCOUNT_TOO_NEW',
message: 'Accounts must be at least 7 days old before deletion',
remediation: `Please wait ${7 - daysSinceCreation} more days`,
});
}
return allow();
},
});

2. Register Your Policy

Extend the default registry:

import { accountDeletionRegistry } from '@kit/account-hooks';
import { gracePeriodPolicy } from './my-policies';
// Add to existing defaults
accountDeletionRegistry.registerPolicy(gracePeriodPolicy);

Context Type

interface AccountDeleteContext {
userId: string; // User attempting to delete account
timestamp: string; // ISO timestamp
}

Customization Patterns

Use Defaults As-Is

import { accountDeletion } from '@kit/account-hooks';
const result = await accountDeletion.run({
userId: user.id,
timestamp: new Date().toISOString(),
});

Extend Defaults

import { accountDeletionRegistry } from '@kit/account-hooks';
import { myCustomPolicy } from './policies';
accountDeletionRegistry.registerPolicy(myCustomPolicy);

Cherry-Pick Policies

import { createPolicyRegistry, createPolicyRuntime } from '@kit/policies';
import { checkOrganizationsPolicy } from '@kit/account-hooks';
const registry = createPolicyRegistry();
// Only check organizations, skip subscriptions
registry.registerPolicy(checkOrganizationsPolicy);
const runtime = createPolicyRuntime(registry);

Replace a Policy

import { createPolicyRegistry, createPolicyRuntime } from '@kit/policies';
import { checkOrganizationsPolicy } from '@kit/account-hooks';
import { mySubscriptionPolicy } from './policies';
const registry = createPolicyRegistry();
registry.registerPolicy(checkOrganizationsPolicy);
registry.registerPolicy(mySubscriptionPolicy); // Your replacement
const runtime = createPolicyRuntime(registry);

Example: Auto-Cancel Subscriptions

Instead of blocking deletion, auto-cancel subscriptions:

import { definePolicy, allow } from '@kit/policies';
export const autoCancelPolicy = definePolicy<AccountDeleteContext>({
id: 'account-deletion.auto-cancel-subscriptions',
evaluate: async (context) => {
const subscriptions = await getActiveSubscriptions(context.userId);
for (const sub of subscriptions) {
await cancelSubscription(sub.id);
}
return allow({
cancelledSubscriptions: subscriptions.length,
});
},
});

Example: Require Data Export

export const dataExportPolicy = definePolicy<AccountDeleteContext>({
id: 'account-deletion.require-export',
evaluate: async (context) => {
const hasExported = await checkDataExportStatus(context.userId);
if (!hasExported) {
return deny({
code: 'DATA_NOT_EXPORTED',
message: 'Please export your data before deleting your account',
remediation: 'Go to Settings > Privacy > Export Data',
});
}
return allow();
},
});

Integration

The policies are evaluated in the Better Auth beforeDeleteUser hook:

// packages/account/hooks/src/before-delete.ts
import { accountDeletion } from './policies/registry';
export async function beforeAccountDelete(userId: string) {
const result = await accountDeletion.run({
userId,
timestamp: new Date().toISOString(),
});
if (!result.allowed) {
throw new Error('Account deletion blocked');
}
}

Next: Preferences →