| 項目 | 内容 |
|---|
| 対象FR | FR-001(ユーザー登録)、FR-002(ログイン) |
| 優先度 | 高 |
| ステータス | 詳細化済み |
概要
ユーザーはメールアドレスを入力し、6桁ワンタイムコードで認証する。登録とログインは同一フローで処理され、未登録メールアドレスの場合は認証成功時にアカウントを自動作成する。
入力
認証フロー
- ユーザーがメールアドレスを入力して送信する
- システムは登録済み/未登録にかかわらず「認証コードを送信しました」と応答する(メールアドレスの登録有無を外部に漏らさない)
- 未登録の場合、認証コード検証成功時にユーザーアカウントを自動作成する
- 認証成功後、セッションCookieを発行しログイン状態となる
認証コード仕様
| 項目 | 値 |
|---|
| 形式 | 数字6桁 |
| 有効期限 | 10分 |
| 最大試行回数 | 3回(超過でコード無効化) |
| 再送間隔 | 60秒に1回 |
| 生成方法 | crypto.getRandomValues による暗号学的に安全な乱数 |
| DB保存 | プレーンテキスト |
※ 新しいコード発行時、同一メールアドレスの既存未使用コードは無効化する
レートリミット
- 同一メールアドレスへの認証コード送信: 60秒に1回
- 同一IPからのリクエスト制限: 1分あたり100リクエスト
セッション管理
| 項目 | 値 |
|---|
| 方式 | セッションCookie |
| Cookie名 | __Host-session(__Host-プレフィックスでSecure + Path=/を強制) |
| Cookie属性 | HttpOnly, Secure, SameSite=Lax |
| セッション有効期限 | 7日 |
| リフレッシュトークン | あり(有効期限30日) |
| トークンローテーション | リフレッシュ時に旧トークンを即無効化 |
| トークン生成 | nanoid(暗号学的に安全な乱数ベース) |
| マルチデバイス | 複数デバイスからの同時ログインを許可(セッション単位で管理) |
ログアウト
- 該当セッションのみ破棄(
POST /api/auth/logout)
- 全デバイスのセッションを一括破棄(
POST /api/auth/logout-all)
メール送信
- 送信失敗時はリトライせずクライアントにエラーを返す
- メール送信サービスは外部サービス(TBD)を利用
- 開発環境ではコンソールログ出力
APIエンドポイント
| メソッド | パス | 概要 |
|---|
| POST | /api/auth/send-code | 認証コード送信(登録/ログイン共通) |
| POST | /api/auth/verify | 認証コード検証・セッション発行 |
| POST | /api/auth/refresh | セッションリフレッシュ |
| POST | /api/auth/logout | 現在のセッションを破棄 |
| POST | /api/auth/logout-all | 全セッションを一括破棄 |
| GET | /api/auth/sessions | アクティブセッション一覧 |
エッジケース
- 既に登録済みのメールアドレスで登録リクエスト → 認証コードを送信し、通常のログインフローとして扱う(登録済みか否かの情報を漏らさない)
- メール確認(verification)は認証フロー自体で兼ねる(別途メール確認ステップは不要)
拡張予定(現時点ではスコープ外)
検証方法
- メールアドレス入力→認証コード受信→コード入力→アカウント作成の一連フローが完了すること
- 無効なコード入力3回で認証コードが無効化されること
- 60秒以内の再送リクエストが拒否されること
- 登録済み/未登録のメールアドレスで応答に差異がないこと
- リフレッシュトークン使用後に旧トークンが無効化されること
- 複数デバイスからの同時ログインが可能であること
- ログアウトで該当セッションのみ破棄されること
- 全デバイスログアウトで全セッションが破棄されること