Skip to content

メールアドレス確認とパスワードリセットを実装する #183

Description

@hirokisakabe

Status: Ready

概要

Better Auth のメールアドレス確認とパスワードリセットを有効化し、両機能で共有するトランザクションメール送信基盤を整備する。メールアドレスの所有を確認したユーザーだけがログインでき、パスワードを忘れたユーザーが Web から安全に再設定できるようにする。

背景 / モチベーション

  • 現在はメールアドレスとパスワードで登録できるが、メールアドレスの所有確認を行っていない
  • パスワードを忘れたユーザーが自力でアカウントへ戻る経路がない
  • メールアドレス確認とパスワードリセットは、送信プロバイダー、メール送信処理、テンプレート、環境変数、失敗監視を共有するため、一つの認証メール基盤として実装する
  • 配送要求が拒否された場合に「送信した」と断定すると、ユーザーが届かないメールを待ち続けるため、配送結果と画面表示を一致させる必要がある
  • パスワード再設定では、応答内容だけでなく処理時間からもアカウントの登録有無を推測しにくくする必要がある

受け入れ条件

  • Resend を導入し、API からメールアドレス確認メールとパスワード再設定メールを送信できる
  • Better Auth の emailVerification.sendVerificationEmailsendOnSignUpsendOnSignInemailAndPassword.requireEmailVerification を公式 API に沿って設定する
  • 新規登録時に確認メールが送信され、確認完了まではセッションが作成されず、画面に次の操作が分かる案内が表示される
  • メール内の有効な確認リンクを開くと確認済みになり、ログイン画面へ直接遷移して確認完了の案内が一度だけ表示される
  • 未確認ユーザーは Web・Mobile・CLI のいずれからもログインできず、各クライアントで確認が必要だと理解できる案内が表示される
  • 未確認ユーザーが正しい資格情報でログインを試みた場合は確認メールの再送を試み、匿名で任意アドレス宛てに再送できるエンドポイントは公開しない
  • 既存の未確認ユーザーも自動的に確認済みとはせず、確認メールによる所有確認後にログインできる
  • Web のログイン画面からパスワード再設定を要求できる
  • パスワード再設定要求は、メールアドレスの登録有無や配送結果をレスポンス・画面・実用上明白な処理時間差から判別できない
  • メール内の有効な再設定リンクから新しいパスワードを設定でき、変更後は既存セッションがすべて失効し、新しいパスワードでログインできる
  • 確認リンクと再設定リンクの有効期限は 1 時間で、使用済み・期限切れ・不正なリンクでは処理されず、再試行や再送など次の操作が分かる
  • メールプロバイダーが配送要求を拒否またはタイムアウトした場合、確認メールについて配送成功を断定する表示が出ず、ユーザーが再試行できる
  • メール送信失敗は、用途・サニタイズ済みの失敗種別・request ID を構造化ログへ記録し、API キー、メールアドレス、確認・再設定トークン、プロバイダーのレスポンス本文をログやクライアントへ露出しない
  • メール本文は text / HTML の両方を用意し、HTML に埋め込む値を安全に扱う
  • RESEND_API_KEY、送信元アドレス、送信元名など必要な設定を .env.example と運用ドキュメントへ追加し、API キーは本番では Secret Manager から Cloud Run へ渡す
  • 未レビューコードから秘密値を読み取られないよう、PR preview にはメール送信資格情報を渡さず、その制約とメール配送の確認方法を文書化する
  • pnpm db:seed で作成する固定テストユーザーは確認済みとしてログインできる
  • メール送信処理を差し替え可能にし、自動テストでは外部へ実メールを送信しない
  • API テストで、新規登録時の送信、未確認ログイン拒否と再送、確認成功、無効・期限切れトークン、登録有無を秘匿した再設定要求、再設定成功、既存セッション失効、配送拒否・タイムアウト時の安全な応答とログを検証する
  • Web テストで、登録後案内、確認成功後の遷移、未確認時案内、再設定要求、再設定成功、無効・期限切れリンク、配送結果を断定できない場合の表示を検証する
  • Mobile・CLI テストで、未確認エラーを利用者向けの案内へ変換できることを検証する
  • lint・format-check・typecheck・test・build が成功する

実装方針

  • メール送信処理を認証設定から分離し、Resend クライアントとテスト用 sender を差し替えられる構成にする
  • Resend の emails.send が返す error も失敗として扱い、プロバイダーの詳細は保持・転送せず、設定不備・拒否・タイムアウト・到達不能・不明などの安全な分類へ変換する
  • Cloud Run でリクエスト終了後の未保証な非同期処理に依存しないよう、プロバイダーによる受付を bounded timeout 内で確認してから応答する
  • パスワード再設定要求は既知・未知アカウントで同じ応答にし、既知アカウントの配送処理にだけ現れる時間差を小さくするため最小応答時間を適用する
  • Better Auth の確認・再設定 callback URL に対応する Web ルートを追加する
  • 確認成功時は中間完了画面を挟まず、状態を表す検索パラメータ付きでログイン画面へ戻す。無効・期限切れ時だけ再送導線を持つエラー画面を表示する
  • Mobile / CLI 専用の確認・再設定画面は追加せず、未確認時はメール内リンクまたは Web で続行できる案内を表示する

スコープ外

  • メールアドレス変更フロー
  • Mobile / CLI 専用のパスワード再設定 UI やディープリンク
  • 二要素認証、パスキー、ソーシャルログイン、パスワードレス認証
  • CAPTCHA / Turnstile などの bot 対策
  • Better Auth のレート制限ストレージの DB / Redis への移行
  • 独自の永続メールキューや自動リトライ基盤
  • メールマガジンや通知メール
  • バウンス・苦情 webhook の運用

参考

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions