# 画面設計書 1-サインイン画面

## 概要

本ドキュメントは、Ghost管理画面のサインイン画面の設計内容を記載する。

### 本画面の処理概要

サインイン画面は、Ghost管理画面にアクセスするためのスタッフユーザー認証機能を提供する画面である。

**業務上の目的・背景**：Ghost管理画面は認証が必要なセキュアな領域であり、正当なスタッフユーザーのみがコンテンツ管理や設定変更を行えるようにする必要がある。本画面はその入口として、メールアドレスとパスワードによる認証を実現し、不正アクセスを防止する役割を担う。また、2段階認証（2FA）への対応、パスワードリセット機能への導線も提供している。

**画面へのアクセス方法**：ブラウザで管理画面URL（`/ghost/`）にアクセスした際、未認証状態であれば本画面にリダイレクトされる。または、セッション期限切れ後の再ログイン時にも本画面が表示される。

**主要な操作・処理内容**：
1. メールアドレス入力 - スタッフユーザーとして登録されているメールアドレスを入力
2. パスワード入力 - 対応するパスワードを入力
3. サインインボタン押下 - 認証処理を実行し、成功時はホーム画面へ遷移
4. パスワードを忘れた場合（Forgot?）リンク - パスワードリセット用のメールを送信

**画面遷移**：
- 遷移元：なし（認証が必要な画面へのアクセス時に自動リダイレクト）
- 遷移先：
  - 認証成功時：ホーム画面
  - 2FA必要時：サインイン確認画面
  - Ghostセットアップ未完了時：セットアップ画面

**権限による表示制御**：本画面は未認証ユーザー専用であり、既にログイン済みの場合はホーム画面にリダイレクトされる。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 88 | 認証 | 主機能 | メールアドレスとパスワードによるスタッフユーザーの認証処理 |
| 40 | スタッフ管理 | 補助機能 | 認証成功時のユーザー情報取得 |

## 画面種別

認証（ログイン）

## URL/ルーティング

- **URL**: `/ghost/signin`
- **Emberルート名**: `signin`
- **ルートファイル**: `ghost/admin/app/routes/signin.js`

## 入出力項目

| 項目名 | 項目ID | 型 | 必須 | 入力/出力 | バリデーション | 備考 |
|--------|--------|-----|------|----------|---------------|------|
| メールアドレス | identification | string | 必須 | 入力 | メール形式チェック | プレースホルダー: "jamie@example.com" |
| パスワード | password | string | 必須 | 入力 | 空チェック | マスク表示 |

## 表示項目

| 項目名 | 表示条件 | データソース | 備考 |
|--------|----------|--------------|------|
| サイトアイコン | 常時 | config.siteIcon | スタイル属性で表示 |
| ブログタイトル | 常時 | config.blogTitle | ヘッダー部に表示 |
| フローエラー/通知 | エラー/通知発生時 | flowErrors, flowNotification | 画面下部に赤字または通常表示 |
| パスワードリセット通知 | passwordResetEmailSent=true時 | - | 認証フォームの代わりに表示 |

## イベント仕様

### 1-サインインボタン押下

**トリガー**: 「Sign in →」ボタンクリックまたはEnterキー押下

**処理フロー**:
1. `validateAndAuthenticateTask`が実行される
2. フォームバリデーション実行（メール形式、必須チェック）
3. バリデーション成功時、`authenticateTask`を実行
4. APIエンドポイント`POST /ghost/api/admin/session`に認証リクエスト送信
5. 認証成功時：セッション作成後、ホーム画面へリダイレクト
6. 2FA要求時：サインイン確認画面へ遷移
7. 認証失敗時：エラーメッセージを表示

**遷移先**:
- 成功時: `/ghost/` (ホーム画面)
- 2FA要求時: `/ghost/signin-verify`

### 2-Forgot?リンク押下

**トリガー**: 「Forgot?」ボタンクリック

**処理フロー**:
1. `forgotPasswordTask`が実行される
2. メールアドレスのバリデーション実行
3. バリデーション成功時、`POST /ghost/api/admin/authentication/password_reset`にリクエスト送信
4. 成功時：「パスワードリセットメールを送信しました」通知表示
5. 失敗時：エラーメッセージを表示

### 3-メールアドレス入力欄のフォーカスアウト

**トリガー**: メールアドレス入力欄からフォーカスが外れた時

**処理フロー**:
1. `validateProperty('identification')`が実行される
2. メール形式のバリデーション実行
3. 不正な形式の場合、エラー表示

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| サインイン成功 | users | UPDATE | last_seen カラムを現在日時で更新 |
| サインイン成功 | users | UPDATE | status を 'active' に更新（warn状態からの復帰） |
| サインイン成功 | sessions | INSERT | 新規セッションレコードの作成 |

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

#### users

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE | last_seen | 現在日時 | ログイン成功時に更新 |
| UPDATE | status | 'active' | warn-1〜warn-4状態から復帰 |

#### sessions

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | user_id | ログインユーザーのID | セッション作成時 |
| INSERT | session_data | セッション情報 | Cookieベース認証用 |

## メッセージ仕様

| メッセージID | 種別 | メッセージ内容 | 表示条件 |
|-------------|------|---------------|----------|
| M001 | エラー | "Email address is not valid" | メールアドレス形式不正 |
| M002 | エラー | "Please enter an email" | メールアドレス未入力 |
| M003 | エラー | "Please enter a password" | パスワード未入力 |
| M004 | エラー | "Please fill out the form to sign in." | バリデーションエラー |
| M005 | エラー | "There is no user with that email address." | 該当ユーザー不在 |
| M006 | エラー | "Your password is incorrect." | パスワード不一致 |
| M007 | エラー | "Your account was suspended." | アカウント停止状態 |
| M008 | エラー | "Access Denied." | 認証拒否（汎用） |
| M009 | 成功 | "An email with password reset instructions has been sent." | パスワードリセットメール送信成功 |
| M010 | エラー | "We need your email address to reset your password." | パスワードリセット時メール未入力 |
| M011 | エラー | "There was a problem on the server." | サーバーエラー |
| M012 | 通知 | "For security, you need to create a new password. An email has been sent to you with instructions." | パスワードリセット必須状態 |

## 例外処理

| 例外ケース | 対応処理 | 表示内容 |
|-----------|----------|----------|
| ユーザー不在 | エラー表示、メールアドレス欄にエラーマーク | "There is no user with that email address." |
| パスワード不一致 | エラー表示、パスワード欄にエラーマーク | "Your password is incorrect." |
| アカウント停止 | エラー表示 | "Your account was suspended." |
| アカウントロック（locked状態） | パスワードリセットメール自動送信、リセット画面表示 | "For security, you need to create a new password..." |
| レート制限超過 | エラー表示 | TooManyRequestsErrorのメッセージ |
| 2FA必要 | サインイン確認画面へ遷移 | - |
| APIバージョン不一致 | 通知表示 | バージョン不一致エラー内容 |
| 通信エラー | 通知表示 | "There was a problem on the server." |

## 備考

- 本画面はEmber.jsで実装されており、ember-simple-authによるCookieベース認証を使用
- セットアップ未完了の場合、本画面にアクセスしてもセットアップ画面にリダイレクトされる
- ブルートフォース対策として、認証成功時にbrute.reset()が呼ばれる
- パスワードリセット必須状態（locked）のユーザーは、認証試行時に自動的にパスワードリセットメールが送信される

---

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

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

### 推奨読解順序

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

まず、サインインに使用されるデータモデルを理解することが重要である。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | signin.js (Route) | `ghost/admin/app/routes/signin.js` | Signinクラスのデータ構造（identification, password, errors）を確認 |
| 1-2 | signin.js (Validator) | `ghost/admin/app/validators/signin.js` | バリデーションルールの定義を確認 |

**読解のコツ**: Ember.jsでは`@tracked`デコレータがリアクティブなプロパティを示す。`Errors`クラスはEmber Dataの内部クラスで、フォームエラー管理に使用される。

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

テンプレートとコントローラーの関係を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | signin.hbs | `ghost/admin/app/templates/signin.hbs` | フォーム構造、イベントバインディング、条件分岐を確認 |
| 2-2 | signin.js (Controller) | `ghost/admin/app/controllers/signin.js` | イベントハンドラとタスクの定義を確認 |

**主要処理フロー**:
1. **15行目**: フォーム送信時に`validateAndAuthenticateTask`が実行される
2. **34-35行目**: 入力イベントで`handleInput`アクションが呼ばれる
3. **55-63行目**: Forgot?ボタンで`forgotPasswordTask`が実行される

#### Step 3: コントローラーのタスク処理を理解する

ember-concurrencyのtaskを使った非同期処理の流れを把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | signin.js (Controller) | `ghost/admin/app/controllers/signin.js` | validateAndAuthenticateTask（112-128行目）、authenticateTask（53-109行目）の処理フロー |

**主要処理フロー**:
- **112-128行目**: バリデーション実行後、authenticateTaskを呼び出し
- **54-56行目**: session.authenticateでember-simple-authの認証を実行
- **59-64行目**: 2FA必要時の分岐処理
- **130-172行目**: パスワードリセットタスクの処理

#### Step 4: 認証処理（Authenticator）を理解する

実際の認証APIコールを行うAuthenticatorの処理を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | cookie.js | `ghost/admin/app/authenticators/cookie.js` | authenticate関数（22-48行目）でAPIコールを確認 |

**主要処理フロー**:
- **35-48行目**: POST /session エンドポイントへの認証リクエスト
- **51-60行目**: invalidate関数でログアウト処理

#### Step 5: バックエンドAPI処理を理解する

サーバーサイドのセッション作成処理を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | session.js | `ghost/core/core/server/api/endpoints/session.js` | add関数（13-63行目）で認証処理全体を確認 |
| 5-2 | user.js | `ghost/core/core/server/models/user.js` | check関数（979行目〜）でパスワード検証処理を確認 |
| 5-3 | index.js | `ghost/core/core/server/services/auth/session/index.js` | sessionServiceの初期化と依存関係を確認 |

**主要処理フロー**:
- **session.js 24-32行目**: User.getByEmailでユーザー取得、User.checkでパスワード検証
- **session.js 34-44行目**: セッション作成ミドルウェアの生成
- **user.js 982-1007行目**: メールでユーザー検索、状態チェック、パスワード検証、last_seen更新

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

```
signin.hbs (テンプレート)
    │
    ├─ SigninController.validateAndAuthenticateTask
    │      ├─ ValidationEngine.validate (signin validator)
    │      └─ SigninController.authenticateTask
    │             └─ session.authenticate('authenticator:cookie')
    │                    └─ CookieAuthenticator.authenticate
    │                           └─ POST /ghost/api/admin/session
    │                                  └─ SessionController.add
    │                                         ├─ User.getByEmail
    │                                         ├─ User.check
    │                                         │      ├─ isPasswordCorrect
    │                                         │      └─ updateLastSeen
    │                                         └─ auth.session.createSession
    │
    └─ SigninController.forgotPasswordTask
           └─ POST /ghost/api/admin/authentication/password_reset
                  └─ AuthenticationController.generateResetToken
```

### データフロー図

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

identification ───────┐
(email)               │
                      ├──▶ validateAndAuthenticateTask ───▶ セッションCookie設定
password ─────────────┘          │                              │
                                 │                              ▼
                                 ▼                         ホーム画面へ遷移
                           authenticateTask                    または
                                 │                         エラー表示
                                 ▼
                           POST /session
                                 │
                                 ▼
                           User.check
                                 │
                                 ▼
                           sessions テーブル
                           users テーブル (last_seen更新)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| signin.hbs | `ghost/admin/app/templates/signin.hbs` | テンプレート | サインインフォームのUI定義 |
| signin.js | `ghost/admin/app/routes/signin.js` | ルート | ルーティングとモデル初期化 |
| signin.js | `ghost/admin/app/controllers/signin.js` | コントローラー | フォーム処理とイベントハンドリング |
| signin.js | `ghost/admin/app/validators/signin.js` | バリデータ | 入力値のバリデーションルール定義 |
| unauthenticated.js | `ghost/admin/app/routes/unauthenticated.js` | ルート（親） | 未認証ルートの基底クラス |
| cookie.js | `ghost/admin/app/authenticators/cookie.js` | 認証 | ember-simple-authのAuthenticator実装 |
| session.js | `ghost/core/core/server/api/endpoints/session.js` | API | セッション管理APIエンドポイント |
| user.js | `ghost/core/core/server/models/user.js` | モデル | ユーザーモデル、パスワード検証 |
| index.js | `ghost/core/core/server/services/auth/session/index.js` | サービス | セッションサービスの初期化 |
| base.js | `ghost/admin/app/validators/base.js` | バリデータ（基底） | バリデーション基底クラス |
| validation-engine.js | `ghost/admin/app/mixins/validation-engine.js` | ミックスイン | バリデーション機能の提供 |
