# 画面設計書 43-メールアドレス確認画面

## 概要

本ドキュメントは、Fizzyアプリケーションにおけるメールアドレス確認画面の設計仕様を定義したものです。ユーザーが確認メール内のリンクをクリックした後に表示される、メールアドレス変更を最終確定するための画面について記載しています。

### 本画面の処理概要

本画面は、メールアドレス変更の確認メール内のリンクをクリックした際に表示される画面です。自動送信フォームにより、ユーザーの操作なしでメールアドレス変更が完了するよう設計されています。

**業務上の目的・背景**：メールアドレス変更のセキュリティを確保するため、確認メール内のリンクをクリックした本人のみがメールアドレスを変更できる仕組みが必要です。本画面はトークンを検証し、有効な場合に自動的にメールアドレス変更を実行します。これにより、新しいメールアドレスの所有者が確実に本人であることを保証します。

**画面へのアクセス方法**：確認メール内のリンク（`user_email_address_confirmation_path`）をクリックしてアクセスします。URLにはユーザーIDとメールアドレス変更用トークンが含まれています。認証なしでアクセス可能です。

**主要な操作・処理内容**：
1. 画面表示と同時に自動送信フォームがトリガーされる
2. トークンが検証され、有効な場合はメールアドレスが変更される
3. 既存のセッションが終了し、新しいセッションが開始される
4. プロフィール編集画面にリダイレクトされる

**画面遷移**：
- 遷移元：確認メール内のリンク
- 遷移先（成功時）：プロフィール編集画面（No.39）
- 遷移先（失敗時）：メールアドレス確認エラー画面（No.44）

**権限による表示制御**：本画面は`allow_unauthenticated_access`が設定されており、認証なしでアクセス可能です。ただし、有効なトークンがないと処理は完了しません。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 72 | メールアドレス確認 | 主機能 | 新しいメールアドレスを確認 |

## 画面種別

確認（自動処理）

## URL/ルーティング

- パス: `/users/:user_id/email_addresses/:token/confirmation`
- HTTPメソッド: GET（表示）/ POST（確認処理）
- ルート名: `user_email_address_confirmation_path`
- コントローラ: `Users::EmailAddresses::ConfirmationsController#show` / `#create`

## 入出力項目

| 項目名 | 項目ID | 種別 | 必須 | 形式 | 最大長 | 説明 |
|--------|--------|------|------|------|--------|------|
| メールアドレス変更トークン | email_address_token | 入力（hidden） | ○ | string | - | URLパラメータから取得 |

## 表示項目

| 項目名 | 説明 | 表示条件 |
|--------|------|----------|
| ページタイトル | "Confirm email change" | 常時 |
| 説明文 | 確認処理中の案内文 | 常時 |
| Doneボタン | 手動送信用ボタン（通常は自動送信） | 常時 |

## イベント仕様

### 1-画面表示時（自動送信）

画面が表示されると、Stimulusコントローラ（`auto-submit`）によりフォームが自動送信されます。これにより、ユーザーの追加操作なしでメールアドレス変更処理が開始されます。

### 2-Doneボタン押下（フォールバック）

自動送信が動作しない場合のフォールバックとして、「Done」ボタンを手動でクリックすることでフォームを送信できます。

### 3-フォーム送信処理

フォーム送信時、以下の処理が実行されます：
1. レート制限チェック（1時間に5回まで）
2. トークンを使用してメールアドレス変更を実行
3. 成功時：既存セッション終了、新規セッション開始、プロフィール編集画面へリダイレクト
4. 失敗時：メールアドレス確認エラー画面を表示

## データベース更新仕様

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| フォーム送信（成功時） | identities | SELECT/INSERT | 新しいメールアドレスでIdentity検索または作成 |
| フォーム送信（成功時） | users | UPDATE | ユーザーのidentity_idを更新 |
| フォーム送信（成功時） | sessions | DELETE | 既存セッションを削除 |
| フォーム送信（成功時） | sessions | INSERT | 新しいセッションを作成 |

### テーブル別更新項目詳細

#### identities

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT/INSERT | email_address | 新しいメールアドレス | find_or_create_by! |
| INSERT | created_at | 現在時刻 | 新規作成時 |
| INSERT | updated_at | 現在時刻 | 新規作成時 |

#### users

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE | identity_id | 新しいIdentityのID | - |
| UPDATE | updated_at | 現在時刻 | - |

#### sessions

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| DELETE | - | Current.sessionを削除 | terminate_session |
| INSERT | identity_id | 新しいIdentityのID | start_new_session_for |
| INSERT | ip_address | リクエストのIPアドレス | - |
| INSERT | user_agent | リクエストのUser-Agent | - |

## メッセージ仕様

| メッセージID | 種別 | メッセージ内容 | 表示条件 |
|-------------|------|---------------|----------|
| MSG-001 | 案内 | Just a sec while we confirm your new email address. | 常時表示 |

## 例外処理

| 例外ケース | 対応 |
|-----------|------|
| トークンが無効または期限切れ | メールアドレス確認エラー画面（invalid_token）を表示 |
| レート制限超過（1時間に5回以上） | 429 Too Many Requests応答 |
| ユーザーが見つからない（非アクティブ含む） | 404エラー |
| トークンの旧メールアドレスと現在のメールアドレスが不一致 | invalid_token画面を表示 |

## 備考

- 本画面は認証不要（`allow_unauthenticated_access`）で設計されている
- トークンの有効期限は30分（`EMAIL_CHANGE_TOKEN_EXPIRATION`）
- 自動送信にはStimulusコントローラ「auto-submit」が使用される
- フォームにはStimulusコントローラ「form」も適用されている
- トークンはSignedGlobalIDを使用して生成・検証される
- メールアドレス変更はトランザクション内で実行される

---

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

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

### 推奨読解順序

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

メールアドレス変更に関わるモデルの関係とトークン構造を理解します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | email_address_changeable.rb | `app/models/user/email_address_changeable.rb` | トークンの生成・検証ロジックを理解 |
| 1-2 | user.rb | `app/models/user.rb` | UserとIdentityの関係を確認 |
| 1-3 | identity.rb | `app/models/identity.rb` | Identityとセッションの関係を確認 |

**読解のコツ**: SignedGlobalIDを使用したトークン生成・検証の仕組みを理解することが重要です。`to_sgid`と`SignedGlobalID.parse`の使い方を確認してください。

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

処理の起点となるコントローラを特定します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | confirmations_controller.rb | `app/controllers/users/email_addresses/confirmations_controller.rb` | showとcreateアクションの実装を確認 |

**主要処理フロー**:
1. **行2**: `allow_unauthenticated_access` - 認証なしでアクセス可能
2. **行5**: `rate_limit to: 5, within: 1.hour, only: :create` - レート制限
3. **行7-8**: `show`アクション - ビューをレンダリング
4. **行10-18**: `create`アクション - トークン検証とメールアドレス変更
5. **行12**: `terminate_session` - 既存セッションの終了
6. **行13**: `start_new_session_for` - 新規セッションの開始

#### Step 3: トークン検証ロジックを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | email_address_changeable.rb | `app/models/user/email_address_changeable.rb` | change_email_address_using_tokenメソッドを詳細に確認 |

**主要処理フロー**:
- **行7-17**: トークンの検証（SignedGlobalID.parse）
- **行10-11**: 旧・新メールアドレスの取得
- **行13-17**: 検証条件（トークン有効性、ユーザー一致、旧メールアドレス一致）
- **行33-37**: `change_email_address`メソッド - トランザクション内でIdentity更新

#### Step 4: ビューテンプレートを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | show.html.erb | `app/views/users/email_addresses/confirmations/show.html.erb` | 自動送信フォームの構造を確認 |

**主要処理フロー**:
- **行11**: `auto-submit`コントローラによる自動送信設定
- **行12**: hiddenフィールドでトークンを送信

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

```
Users::EmailAddresses::ConfirmationsController#show
    │
    └─ show.html.erb レンダリング
           │
           └─ auto-submit (Stimulus) ──▶ フォーム自動送信

Users::EmailAddresses::ConfirmationsController#create
    │
    ├─ set_user (before_action)
    │      └─ Current.account.users.active.find(params[:user_id])
    │
    ├─ @user.change_email_address_using_token(token)
    │      │
    │      ├─ SignedGlobalID.parse(token)
    │      │
    │      ├─ トークン検証
    │      │      ├─ parsed_token.find == self
    │      │      └─ identity.email_address == old_email_address
    │      │
    │      └─ change_email_address(new_email_address)
    │             │
    │             ├─ Identity.find_or_create_by!(email_address:)
    │             │
    │             └─ update!(identity: new_identity)
    │
    ├─ terminate_session (成功時)
    │
    ├─ start_new_session_for @user.identity (成功時)
    │
    └─ redirect_to edit_user_url (成功時)
       または
       render :invalid_token (失敗時)
```

### データフロー図

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

URLパラメータ          ConfirmationsController#create
email_address_token ───▶        │
                                ├─▶ SignedGlobalID.parse ──▶ 旧・新メールアドレス抽出
                                │
                                ├─▶ トークン検証
                                │      │
                                │      ├─（有効）──▶ Identity更新 ──▶ セッション再生成
                                │      │                              │
                                │      │                              └─▶ edit_user_path
                                │      │                                   へリダイレクト
                                │      │
                                │      └─（無効）──▶ invalid_token.html.erb
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| confirmations_controller.rb | `app/controllers/users/email_addresses/confirmations_controller.rb` | コントローラ | 画面制御・トークン検証 |
| show.html.erb | `app/views/users/email_addresses/confirmations/show.html.erb` | ビュー | 確認画面の表示 |
| invalid_token.html.erb | `app/views/users/email_addresses/confirmations/invalid_token.html.erb` | ビュー | エラー画面の表示 |
| email_address_changeable.rb | `app/models/user/email_address_changeable.rb` | Concern | トークン検証・メールアドレス変更ロジック |
| user.rb | `app/models/user.rb` | モデル | ユーザーエンティティ |
| identity.rb | `app/models/identity.rb` | モデル | 認証情報エンティティ |
| routes.rb | `config/routes.rb` | 設定 | ルーティング定義（行21-23） |
