# 機能設計書 4-パスワードリセット

## 概要

本ドキュメントは、Chatwootシステムにおけるパスワードリセット機能の設計を記載する。メール経由でのパスワード変更ワークフローを実現する。

### 本機能の処理概要

本機能は、ユーザーがパスワードを忘れた場合に、メールアドレス宛にリセット用リンクを送信し、安全にパスワードを再設定できる機能を提供する。

**業務上の目的・背景**：ユーザーがパスワードを忘れた場合、業務を継続するためには迅速なパスワード回復が必要である。本機能により、管理者の介入なしにユーザー自身でパスワードをリセットできるようにし、サポート業務の効率化とユーザー体験の向上を実現する。

**機能の利用シーン**：
- ユーザーがパスワードを忘れてログインできない場合
- セキュリティ上の理由でパスワードを変更したい場合
- 長期間未使用でパスワードを失念した場合

**主要な処理内容**：
1. パスワードリセット要求の受付（メールアドレス入力）
2. リセットトークン生成とメール送信
3. リセットトークンの検証
4. 新しいパスワードの設定と保存
5. 自動ログインとセッション確立

**関連システム・外部連携**：
- メール送信システム（Action Mailer / Devise Mailer）
- Active Job（非同期メール送信）

**権限による制御**：パスワードリセットは認証前の操作であり、権限制御は適用されない。リセットトークンの有効性のみで操作を許可する。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 4 | パスワードリセット画面 | 主画面 | パスワードリセット用メール送信 |
| 5 | パスワード編集画面 | 主画面 | 新しいパスワードの設定 |

## 機能種別

認証処理 / パスワード管理

## 入力仕様

### 入力パラメータ

#### リセット要求（create）

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| email | String | Yes | ユーザーメールアドレス | 有効なメール形式 |

#### パスワード更新（update）

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| reset_password_token | String | Yes | リセットトークン | トークン有効性確認 |
| password | String | Yes | 新しいパスワード | パスワードポリシー準拠 |
| password_confirmation | String | Yes | パスワード確認 | password と一致 |

### 入力データソース

- 画面入力（パスワードリセット画面、パスワード編集画面）
- URLパラメータ（リセットトークン）

## 出力仕様

### 出力データ

#### リセット要求成功

| 項目名 | 型 | 説明 |
|--------|-----|------|
| message | String | 成功メッセージ |

#### パスワード更新成功

| 項目名 | 型 | 説明 |
|--------|-----|------|
| access-token | String | APIアクセス用トークン（HTTPヘッダー） |
| client | String | クライアント識別子（HTTPヘッダー） |
| uid | String | ユーザー識別子（HTTPヘッダー） |
| data | Object | ユーザー情報 |

### 出力先

- HTTPレスポンスボディ（JSON形式）
- メール送信（パスワードリセット手順）
- データベース（usersテーブル - パスワードハッシュ、リセットトークン）

## 処理フロー

### 処理シーケンス

```
【リセット要求フロー】
1. リセット要求受信
   └─ PasswordsController#createアクション
   └─ emailパラメータ取得

2. ユーザー検索
   └─ User.from_emailでユーザー検索
   └─ 存在しない場合: 404エラー返却

3. リセットトークン生成・メール送信
   └─ user.send_reset_password_instructions
   └─ リセットトークン生成（Devise内部）
   └─ reset_password_token, reset_password_sent_atを更新
   └─ DeviseMailerでメール送信（非同期）

4. レスポンス返却
   └─ 成功メッセージ返却（200）

【パスワード更新フロー】
5. トークン検証
   └─ PasswordsController#updateアクション
   └─ reset_password_tokenのダイジェスト計算
   └─ usersテーブルから該当ユーザー検索

6. パスワード更新
   └─ user.reset_passwordでパスワード更新
   └─ メール未確認の場合は自動確認
   └─ reset_password_token, confirmation_token, reset_password_sent_atをクリア

7. 自動ログイン
   └─ 認証トークン生成
   └─ HTTPヘッダーにトークン設定
   └─ ユーザー情報返却
```

### フローチャート

```mermaid
flowchart TD
    A[パスワードリセット要求] --> B[email入力]
    B --> C[User.from_email]
    C --> D{ユーザー存在?}
    D -->|No| E[404エラー]
    D -->|Yes| F[send_reset_password_instructions]
    F --> G[リセットトークン生成]
    G --> H[メール送信]
    H --> I[200 OK]

    J[パスワード編集画面] --> K[新パスワード入力]
    K --> L[reset_password_token検証]
    L --> M{トークン有効?}
    M -->|No| N[422エラー: Invalid token]
    M -->|Yes| O[reset_password実行]
    O --> P{メール確認済み?}
    P -->|No| Q[自動確認]
    Q --> R[トークンクリア]
    P -->|Yes| R
    R --> S[認証トークン生成]
    S --> T[200 OK + 認証情報]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-001 | トークン一意性 | リセットトークンはユーザーごとに一意 | トークン生成時 |
| BR-002 | トークン有効期限 | リセットトークンは一定時間で失効（Devise設定依存） | トークン検証時 |
| BR-003 | 自動メール確認 | パスワードリセット成功時、未確認メールも確認済みに | パスワード更新成功時 |
| BR-004 | 自動ログイン | パスワードリセット成功後は自動的にログイン状態に | パスワード更新成功時 |
| BR-005 | トークン無効化 | パスワード更新成功後、リセットトークンは無効化 | パスワード更新成功時 |

### 計算ロジック

- **リセットトークン生成**: Deviseの`set_reset_password_token`メソッドで生成
- **トークンダイジェスト**: `Devise.token_generator.digest`でSHA256ハッシュ化

## データベース操作仕様

### 操作別データベース影響一覧

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| ユーザー検索 | users | SELECT | email条件でユーザー取得 |
| リセットトークン生成 | users | UPDATE | reset_password_token, reset_password_sent_at設定 |
| トークン検索 | users | SELECT | reset_password_token条件でユーザー取得 |
| パスワード更新 | users | UPDATE | encrypted_password更新 |
| トークンクリア | users | UPDATE | reset_password_token, confirmation_token, reset_password_sent_atクリア |

### テーブル別操作詳細

#### users

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | * | WHERE email = :email | 小文字変換後の値で検索 |
| UPDATE | reset_password_token | ダイジェスト化されたトークン | リセット要求時 |
| UPDATE | reset_password_sent_at | Time.now | リセット要求時 |
| SELECT | * | WHERE reset_password_token = :digest | トークン検証時 |
| UPDATE | encrypted_password | bcryptハッシュ | パスワード更新時 |
| UPDATE | confirmed_at | Time.now | 未確認ユーザーの場合 |
| UPDATE | reset_password_token | nil | パスワード更新成功時 |
| UPDATE | confirmation_token | nil | パスワード更新成功時 |
| UPDATE | reset_password_sent_at | nil | パスワード更新成功時 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 404 | Not Found | 指定メールアドレスのユーザーが存在しない | 正しいメールアドレスで再試行 |
| 422 | Unprocessable Entity | リセットトークンが無効 | 新しいリセット要求を送信 |
| 422 | Unprocessable Entity | パスワードがポリシーに適合しない | パスワードポリシーを確認 |
| 422 | Unprocessable Entity | password と password_confirmation が不一致 | 入力値を確認 |

### リトライ仕様

- リセット要求は何度でも可能（前回のトークンは上書き）
- パスワード更新失敗時は同じトークンで再試行可能（有効期限内）

## トランザクション仕様

- パスワード更新とトークンクリアは単一トランザクション
- メール送信は非同期（Active Job経由）

## パフォーマンス要件

- リセット要求処理: 500ms以内
- メール送信: 非同期のため即座にレスポンス返却
- パスワード更新処理: 500ms以内

## セキュリティ考慮事項

- リセットトークンはSHA256でダイジェスト化してデータベースに保存
- メールに含まれるのは生のトークン（ダイジェスト前）
- トークンは一度使用すると無効化
- パスワードはbcryptでハッシュ化
- 存在しないメールアドレスでも処理時間を一定にしてタイミング攻撃を防止

## 備考

- パスワードポリシーは`devise :password_has_required_content`で設定
- リセットメールのテンプレートは`app/views/devise/mailer/`に配置
- トークン有効期限は`config/initializers/devise.rb`で設定

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

パスワードリセット関連のデータベースカラムを把握。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | user.rb | `app/models/user.rb` | reset_password_token, reset_password_sent_at カラム |

**読解のコツ**:
- 18-19行目: reset_password_sent_at, reset_password_token カラム定義
- 61行目: :recoverable モジュール（パスワードリセット機能）

#### Step 2: エントリーポイントを理解する

パスワードリセットコントローラーの処理フローを把握。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | passwords_controller.rb | `app/controllers/devise_overrides/passwords_controller.rb` | create, update アクション |

**主要処理フロー**:
1. **7-15行目**: createアクションでリセット要求処理
2. **8行目**: User.from_emailでユーザー検索
3. **10行目**: send_reset_password_instructionsでメール送信
4. **17-28行目**: updateアクションでパスワード更新
5. **20-21行目**: トークンダイジェスト計算とユーザー検索
6. **32-39行目**: reset_password_and_confirmationでパスワードリセットと確認

#### Step 3: 認証ヘルパーを理解する

レスポンスに認証トークンを付与する処理を把握。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | auth_helper.rb | `app/helpers/auth_helper.rb` | send_auth_headers メソッド |

**主要処理フロー**:
- send_auth_headersでHTTPヘッダーに認証トークンを設定

### プログラム呼び出し階層図

```
DeviseOverrides::PasswordsController
    │
    ├─ create (リセット要求)
    │      ├─ User.from_email
    │      │      └─ User.find_by(email: email.downcase)
    │      │
    │      └─ user.send_reset_password_instructions
    │             ├─ set_reset_password_token
    │             └─ DeviseMailer.reset_password_instructions
    │                    └─ deliver_later (Active Job)
    │
    └─ update (パスワード更新)
           ├─ Devise.token_generator.digest
           │      └─ SHA256ダイジェスト計算
           │
           ├─ User.find_by(reset_password_token: digest)
           │
           └─ reset_password_and_confirmation
                  ├─ user.confirm (未確認の場合)
                  ├─ user.reset_password
                  │      └─ encrypted_password更新
                  └─ user.save!
                         └─ reset_password_token = nil
                         └─ confirmation_token = nil
                         └─ reset_password_sent_at = nil
```

### データフロー図

```
[入力]                    [処理]                         [出力]

【リセット要求】
email ───────────────▶ PasswordsController#create
                              │
                              ▼
                       User.from_email ────────────▶ reset_password_token (DB)
                              │                      reset_password_sent_at (DB)
                              ▼
                       send_reset_password_instructions
                              │
                              ▼
                       DeviseMailer ───────────────▶ Email (パスワードリセットリンク)
                              │
                              ▼
                       200 OK + message ───────────▶ JSON Response

【パスワード更新】
reset_password_token ─▶ PasswordsController#update
password                      │
password_confirmation         ▼
                       Devise.token_generator.digest
                              │
                              ▼
                       User.find_by(token) ────────▶ encrypted_password (DB)
                              │                      confirmed_at (DB)
                              ▼
                       reset_password_and_confirmation
                              │
                              ▼
                       send_auth_headers ──────────▶ HTTPヘッダー (認証トークン)
                              │
                              ▼
                       200 OK + user data ─────────▶ JSON Response
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| passwords_controller.rb | `app/controllers/devise_overrides/passwords_controller.rb` | コントローラー | パスワードリセットAPI |
| user.rb | `app/models/user.rb` | モデル | パスワードリセット関連カラム |
| auth_helper.rb | `app/helpers/auth_helper.rb` | ヘルパー | 認証トークン付与 |
| routes.rb | `config/routes.rb` | 設定 | パスワードルート定義（5行目） |
| reset_password_instructions.html.erb | `app/views/devise/mailer/` | ビュー | リセットメールテンプレート |
| devise.rb | `config/initializers/devise.rb` | 設定 | トークン有効期限等の設定 |
