家計簿アプリを作る #61:アカウント削除機能

家計簿アプリ作成シリーズの第61回です。これまでパスワード変更はできましたが、アカウント自体を削除する手段がありませんでした。設定ページに、アカウントと関連データを完全に削除できる機能を追加します。

パスワード再認証にはchangePasswordを転用する

Better Authには単体の「パスワード検証専用API」が見当たりませんでした。ただし changePassword は内部で「currentPassword が現在のパスワードと一致するか」を検証してから変更処理に進む仕組みなので、currentPasswordnewPassword に同じ値を渡せば、実質的にパスワードは変わらないまま検証だけを行えます。

const auth = createAuth(platform!.env.DB);
try {
  await auth.api.changePassword({
    body: { currentPassword: password, newPassword: password },
    headers: request.headers,
  });
} catch {
  return fail(400, { error: 'パスワードが正しくありません' });
}

やや技巧的な使い方ですが、専用APIが無い以上、現実的な回避策です。

削除の順序を考える

auth.api.deleteUser() を先に呼んでからアプリ独自のテーブル(transactions など)を削除すると、DB削除の途中で失敗した場合に「ユーザーは消えたのにデータだけ残る」という不整合が起きえます。逆にDB削除を先にすると、パスワードが間違っていた場合に「データは消えたのにユーザーは残る」という不整合が起きえます。

完全な原子性を保証するのは難しいため、今回は以下の順序に落ち着きました。

  1. パスワードを検証する(changePasswordの転用)
  2. アプリ独自データを削除する(transactions, categories, budgets, recurringTransactions, monthlyBudgets)
  3. Better Authのユーザーを削除する
  4. セッションを破棄してログインページへリダイレクトする

パスワード検証を最初に切り離すことで、少なくとも「パスワードが間違っているのに何かが消える」という最悪のケースは避けられます。

ハマりポイント:Delete userはデフォルトで無効

実装後、動作確認すると次のエラーになりました。

ERROR [Better Auth]: Delete user is disabled. Enable it in the options

Better Authではアカウント削除機能はデフォルトで無効になっており、betterAuth() の設定で明示的に有効化する必要がありました。

// $lib/server/auth.ts
export function createAuth(d1: D1Database) {
  const db = createDb(d1);
  return betterAuth({
    baseURL: process.env.BETTER_AUTH_URL,
    database: drizzleAdapter(db, { provider: 'sqlite', schema: { ...schema, ...authSchema } }),
    emailAndPassword: { enabled: true },
    secret: process.env.BETTER_AUTH_SECRET,
    user: {
      deleteUser: {
        enabled: true,
      },
    },
  });
}

削除のような破壊的な操作はライブラリ側でもデフォルト無効になっていることがあるので、エラーメッセージをよく読んで設定を確認する必要がありました。

確認ダイアログを忘れずに

これまでの削除操作(取引削除・一括削除)と同じく、confirm() による誤操作防止も追加しました。

<Button
  type="submit"
  onclick={(e: Event) => {
    if (!confirm("本当にアカウントを削除しますか?この操作は取り消せません")) {
      e.preventDefault();
    }
  }}>アカウントの削除</Button
>

まとめ

ポイント 内容
パスワード再認証 専用APIが無ければ、changePasswordを同じ値で呼んで検証だけ行う
削除の順序 パスワード検証 → アプリ独自データ削除 → ユーザー削除の順で被害を最小化する
Better Authの設定 user.deleteUser.enabled: true を明示しないとdeleteUserは使えない
誤操作防止 破壊的な操作にはconfirm()を忘れずに付ける
← トップページに戻る