# 画面設計書 1-ログイン画面

## 概要

本ドキュメントは、Chatwootアプリケーションのログイン画面に関する設計書です。ユーザーがシステムにアクセスするための認証機能を提供する画面について記述しています。

### 本画面の処理概要

ログイン画面は、Chatwootアプリケーションへのアクセスを認証するための入り口となる画面です。ユーザーはメールアドレスとパスワードを使用して認証を行い、システムへのアクセス権限を取得します。

**業務上の目的・背景**：カスタマーサポートプラットフォームとしてのChatwootは、顧客情報や会話履歴など機密性の高いデータを扱うため、適切なユーザー認証が不可欠です。本画面は、正当なユーザーのみがシステムにアクセスできるようにするセキュリティの最前線として機能します。また、シングルサインオン（SSO）やGoogle OAuthなど複数の認証方式に対応することで、企業の既存認証基盤との統合や利便性向上を実現しています。

**画面へのアクセス方法**：ブラウザで `/app/login` にアクセスすることで画面が表示されます。未認証状態でダッシュボードにアクセスしようとした場合も、自動的にこの画面にリダイレクトされます。また、SSOトークン付きのURLパラメータ（`sso_auth_token`、`sso_account_id`等）でアクセスした場合は、自動ログイン処理が実行されます。

**主要な操作・処理内容**：
1. メールアドレスとパスワードを入力してログインボタンを押下し、認証を実行する
2. Google OAuthボタンをクリックしてGoogleアカウントで認証する
3. SAMLログインリンクをクリックしてSSOログイン画面に遷移する
4. パスワードを忘れた場合のリンクからパスワードリセット画面に遷移する
5. 新規アカウント作成リンクからサインアップ画面に遷移する
6. MFA（多要素認証）が有効な場合、TOTP入力画面でワンタイムパスワードを入力する

**画面遷移**：
- 遷移元：直接URL入力、未認証状態でのダッシュボードアクセス、ログアウト後
- 遷移先：ダッシュボードホーム画面（認証成功時）、SSOログイン画面、サインアップ画面、パスワードリセット画面、アカウント停止画面、アカウントなし画面

**権限による表示制御**：
- サインアップリンク：`signupEnabled`設定がtrueの場合のみ表示
- Google OAuthボタン：`allowedLoginMethods`に`google_oauth`が含まれ、`googleOAuthClientId`が設定されている場合のみ表示
- SAMLログインリンク：`allowedLoginMethods`に`saml`が含まれる場合のみ表示（Enterprise版のみ）

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 1 | ユーザー認証 | 主機能 | メール・パスワードによるログイン認証処理 |
| 2 | OmniAuth統合 | 補助機能 | Google OAuth2によるソーシャルログイン |
| 3 | 多要素認証（MFA） | 補助機能 | MFA検証が必要な場合のTOTP認証処理 |
| 6 | SSO認証 | 遷移先機能 | SAMLログイン画面への遷移リンク |

## 画面種別

ログイン（認証）

## URL/ルーティング

| パス | ルート名 | 説明 |
|------|----------|------|
| `/app/login` | `login` | ログイン画面 |

### クエリパラメータ

| パラメータ | 型 | 説明 |
|-----------|-----|------|
| `sso_auth_token` | String | SSOトークン（自動ログイン用） |
| `sso_account_id` | String | SSOアカウントID |
| `sso_conversation_id` | String | SSO会話ID（特定会話への直接遷移用） |
| `email` | String | プリセットメールアドレス |
| `error` | String | エラーコード（`no-account-found`, `business-account-only`, `saml-authentication-failed`, `saml-not-enabled`） |
| `impersonation` | String | インパーソネーションモードフラグ |

## 入出力項目

### 入力項目

| 項目名 | 項目ID | 型 | 必須 | バリデーション | 説明 |
|--------|--------|-----|------|----------------|------|
| メールアドレス | `email` | text | Yes | required, email形式 | ログイン用メールアドレス |
| パスワード | `password` | password | Yes | required | ログイン用パスワード |

### 出力項目

認証成功時はダッシュボードにリダイレクトされるため、本画面での出力項目はありません。

## 表示項目

| 項目名 | 説明 | 表示条件 |
|--------|------|----------|
| ロゴ | アプリケーションロゴ（ライト/ダークモード対応） | 常時 |
| タイトル | 「{installationName}にログイン」 | 常時 |
| サインアップリンク | 「または新規アカウントを作成」 | `signupEnabled`がtrueの場合 |
| Google OAuthボタン | Googleでログインボタン | Google OAuth設定済みの場合 |
| SAMLログインリンク | SSO認証ボタン | SAML設定済みの場合 |
| メールアドレス入力欄 | ログイン用メールアドレス入力 | 常時（emailパラメータなしの場合） |
| パスワード入力欄 | ログイン用パスワード入力 | 常時（emailパラメータなしの場合） |
| パスワード忘れリンク | パスワードリセット画面へのリンク | `disableUserProfileUpdate`がfalseの場合 |
| ログインボタン | ログイン実行ボタン | 常時 |
| ローディングスピナー | SSOトークン自動ログイン時 | emailパラメータありの場合 |
| MFA入力画面 | TOTP入力フォーム | MFA検証が必要な場合 |

## イベント仕様

### 1-ログインボタン押下

**トリガー**: ログインボタンのクリックまたはフォーム送信

**処理フロー**:
1. フロントエンドでバリデーション実行
   - メールアドレス形式チェック
   - 必須項目チェック
2. バリデーションエラー時はアラートメッセージ表示
3. API `POST /api/v1/auth/sign_in` に認証情報を送信
4. レスポンスに応じた処理:
   - 認証成功: 認証情報をローカルストレージに保存し、ダッシュボードにリダイレクト
   - MFA必要: MFA入力画面に切り替え
   - 認証失敗: エラーメッセージ表示

**データフロー**:
```
[ユーザー入力]
    │
    ▼
[バリデーション] ──エラー──▶ [アラート表示]
    │
    │ 成功
    ▼
[login API呼び出し]
    │
    ├─ MFA必要 ─▶ [MFA入力画面表示]
    │
    ├─ 成功 ─▶ [認証情報保存] → [ダッシュボードへリダイレクト]
    │
    └─ 失敗 ─▶ [エラーアラート表示]
```

### 2-Google OAuthボタン押下

**トリガー**: Google OAuthボタンのクリック

**処理フロー**:
1. Googleの認証ページにリダイレクト
2. Google側で認証処理
3. コールバックURLに認証結果とともにリダイレクト
4. 認証成功時はダッシュボードにリダイレクト
5. 認証失敗時はエラーパラメータ付きでログイン画面に戻る

### 3-SAMLログインリンク押下

**トリガー**: SAMLログインリンクのクリック

**処理フロー**:
1. `/app/login/sso` にルーター遷移
2. SSOログイン画面を表示

### 4-パスワード忘れリンク押下

**トリガー**: パスワード忘れリンクのクリック

**処理フロー**:
1. `/app/auth/reset/password` にルーター遷移
2. パスワードリセット画面を表示

### 5-サインアップリンク押下

**トリガー**: サインアップリンクのクリック

**処理フロー**:
1. `/app/auth/signup` にルーター遷移
2. サインアップ画面を表示

### 6-MFA検証

**トリガー**: MFA入力画面でのワンタイムパスワード送信

**処理フロー**:
1. TOTP検証API呼び出し
2. 検証成功時: ダッシュボードにリダイレクト
3. 検証失敗時: エラーメッセージ表示
4. キャンセル時: 通常ログイン画面に戻る

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| ログイン成功 | users | UPDATE | `sign_in_count`、`current_sign_in_at`、`last_sign_in_at`、`current_sign_in_ip`、`last_sign_in_ip`の更新 |
| ログイン失敗 | users | UPDATE | `failed_attempts`のインクリメント |
| MFA検証 | - | - | トークン検証のみ、DB更新なし |

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

#### users

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE | sign_in_count | +1 | ログイン成功時 |
| UPDATE | current_sign_in_at | 現在時刻 | ログイン成功時 |
| UPDATE | last_sign_in_at | 旧current_sign_in_at | ログイン成功時 |
| UPDATE | current_sign_in_ip | リクエスト元IP | ログイン成功時 |
| UPDATE | last_sign_in_ip | 旧current_sign_in_ip | ログイン成功時 |
| UPDATE | failed_attempts | +1 | ログイン失敗時 |

## メッセージ仕様

| メッセージID | メッセージ種別 | メッセージ内容 | 表示条件 |
|-------------|---------------|---------------|----------|
| LOGIN.API.SUCCESS_MESSAGE | 成功 | ログインに成功しました | 認証成功時 |
| LOGIN.API.UNAUTH | エラー | 認証に失敗しました | 認証失敗時 |
| LOGIN.EMAIL.ERROR | エラー | メールアドレスが無効です | メールアドレスバリデーションエラー時 |
| LOGIN.OAUTH.NO_ACCOUNT_FOUND | エラー | アカウントが見つかりません | OAuth認証でアカウントなしの場合 |
| LOGIN.OAUTH.BUSINESS_ACCOUNTS_ONLY | エラー | ビジネスアカウントのみ使用できます | 個人アカウントでのOAuth認証時 |
| LOGIN.SAML.API.ERROR_MESSAGE | エラー | SAML認証に失敗しました | SAML認証エラー時 |

## 例外処理

| 例外条件 | 状態・動作 |
|---------|----------|
| ネットワークエラー | 「認証に失敗しました」エラーメッセージを表示 |
| サーバーエラー（500系） | 「認証に失敗しました」エラーメッセージを表示 |
| アカウントロック | アカウントロックメッセージを表示 |
| MFAトークン期限切れ | 再度ログインが必要であるメッセージを表示 |
| SSOトークン無効 | `/app/login`にリダイレクト |
| アカウント停止 | アカウント停止画面にリダイレクト |
| アカウントなし | アカウントなし画面にリダイレクト |

## 備考

- SSOトークン付きでアクセスした場合は、自動的にログイン処理が開始され、ローディング表示となる
- インパーソネーションモード（管理者による代理ログイン）の場合、セッションストレージにフラグが設定され、ユーザー設定の変更が制限される
- ダークモードに対応しており、システム設定に応じてロゴ画像が切り替わる
- パスワード入力欄は`disableUserProfileUpdate`設定によってパスワード忘れリンクの表示が制御される

---

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

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

### 推奨読解順序

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

まず、認証処理で扱われるデータ構造を理解することが重要です。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | apiClient.js | `app/javascript/v3/api/apiClient.js` | APIクライアントの基本設定、ベースURLの構成 |
| 1-2 | auth.js | `app/javascript/v3/api/auth.js` | 認証API関数の定義、リクエスト/レスポンス形式 |

**読解のコツ**: Vue 3とVuexを使用しているため、リアクティブなデータの流れに注目してください。認証情報は`setAuthCredentials`でローカルストレージに保存されます。

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

処理の起点となるVueコンポーネントを特定します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | Index.vue | `app/javascript/v3/views/login/Index.vue` | ログイン画面のメインコンポーネント |
| 2-2 | routes.js | `app/javascript/v3/views/routes.js` | ルーティング定義、propsの受け渡し |

**主要処理フロー**:
1. **行103-118**: `created`ライフサイクルフックでSSOトークンまたはエラーの初期処理
2. **行160-196**: `submitLogin`メソッドでログインAPI呼び出しとレスポンス処理
3. **行198-205**: `submitFormLogin`メソッドでバリデーション後のログイン処理開始
4. **行206-216**: MFA検証関連のハンドラメソッド

#### Step 3: 認証API層を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | auth.js | `app/javascript/v3/api/auth.js` | login関数の実装（行12-47） |
| 3-2 | api.js (store utils) | `app/javascript/dashboard/store/utils/api.js` | 認証情報の保存処理 |
| 3-3 | AuthHelper.js | `app/javascript/v3/helpers/AuthHelper.js` | リダイレクトURL生成ロジック |

**主要処理フロー**:
- **auth.js 行18**: `POST auth/sign_in` エンドポイントへのリクエスト
- **auth.js 行21-27**: MFA必要時の206ステータスハンドリング
- **auth.js 行29-35**: 認証成功時の認証情報保存とリダイレクト

#### Step 4: バックエンドAPI層を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | sessions_controller.rb | `app/controllers/devise_overrides/sessions_controller.rb` | ログインセッション制御 |
| 4-2 | routes.rb | `config/routes.rb` | 認証関連ルーティング |

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

```
Index.vue (ログイン画面)
    │
    ├─ submitFormLogin()
    │      │
    │      └─ submitLogin()
    │             │
    │             └─ login() [auth.js]
    │                    │
    │                    ├─ wootAPI.post('auth/sign_in')
    │                    │      │
    │                    │      └─ [Backend] sessions_controller#create
    │                    │
    │                    ├─ [MFA Required] → mfaRequired = true
    │                    │
    │                    └─ [Success] setAuthCredentials() → redirect
    │
    ├─ GoogleOAuthButton
    │      └─ Google OAuth認証フロー
    │
    └─ MfaVerification
           └─ handleMfaVerified() → redirect
```

### データフロー図

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

credentials.email    ──▶   Index.vue                    ──▶   ダッシュボード
credentials.password       (submitFormLogin)                   リダイレクト
                              │
                              ▼
                          login() [auth.js]
                              │
                              ▼
                          POST /api/v1/auth/sign_in
                              │
                              ▼
                          [Backend]
                          sessions_controller
                              │
                    ┌─────────┼─────────┐
                    ▼         ▼         ▼
                [Success]  [MFA]    [Error]
                    │         │         │
                    ▼         ▼         ▼
              認証情報保存  MFA画面   エラー表示
              localStorage  表示
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| Index.vue | `app/javascript/v3/views/login/Index.vue` | ソース | ログイン画面メインコンポーネント |
| routes.js | `app/javascript/v3/views/routes.js` | ソース | ルーティング定義 |
| auth.js | `app/javascript/v3/api/auth.js` | ソース | 認証API関数 |
| apiClient.js | `app/javascript/v3/api/apiClient.js` | ソース | APIクライアント設定 |
| AuthHelper.js | `app/javascript/v3/helpers/AuthHelper.js` | ソース | 認証ヘルパー関数 |
| FormInput.vue | `app/javascript/v3/components/Form/Input.vue` | ソース | 入力フォームコンポーネント |
| GoogleOAuthButton.vue | `app/javascript/v3/components/GoogleOauth/Button.vue` | ソース | Google OAuth ボタン |
| MfaVerification.vue | `app/javascript/dashboard/components/auth/MfaVerification.vue` | ソース | MFA検証コンポーネント |
| api.js | `app/javascript/dashboard/store/utils/api.js` | ソース | 認証情報保存ユーティリティ |
| sessions_controller.rb | `app/controllers/devise_overrides/sessions_controller.rb` | ソース | ログインセッション制御 |
| en.json | `app/javascript/dashboard/i18n/locale/en/login.json` | 設定 | i18n翻訳ファイル |
