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:
| Policy | ID | Behavior |
|---|---|---|
checkSubscriptionsPolicy | account-deletion.check-subscriptions | Blocks if user has active subscriptions |
checkOrganizationsPolicy | account-deletion.check-organizations | Blocks if sole owner of orgs with other members |
Adding Custom Policies
1. Create Your Policy
// In your app or a custom packageimport { 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 defaultsaccountDeletionRegistry.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 subscriptionsregistry.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 replacementconst 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.tsimport { 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 →