# 画面設計書 5-サインアウト

## 概要

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

### 本画面の処理概要

サインアウトは、ログイン中のスタッフユーザーをログアウトさせるための機能である。専用の画面は存在せず、URLへのアクセスをトリガーとしてセッション無効化処理を実行し、サインイン画面へリダイレクトする。

**業務上の目的・背景**：ユーザーが明示的にログアウトすることで、共有PC環境などでのセキュリティリスクを軽減する。また、別のアカウントでログインし直す場合や、セッション問題のトラブルシューティング時にも使用される。

**画面へのアクセス方法**：
- 管理画面内のユーザーメニューから「Sign out」をクリック
- 直接URL（`/ghost/signout`）にアクセス

**主要な操作・処理内容**：
1. 全ての通知をクリア
2. セッションを無効化（サーバー側でセッション削除）
3. サインイン画面へリダイレクト

**画面遷移**：
- 遷移元：管理画面内の任意の画面（ユーザーメニュー）
- 遷移先：サインイン画面

**権限による表示制御**：認証済みユーザーのみアクセス可能。未認証状態でアクセスするとサインイン画面にリダイレクトされる。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 88 | 認証 | 主機能 | セッションの終了とログアウト処理 |

## 画面種別

処理実行（画面なし）

## URL/ルーティング

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

## 入出力項目

本機能は画面を持たないため、入出力項目は存在しない。

## 表示項目

本機能は画面を持たないため、表示項目は存在しない。

## イベント仕様

### 1-サインアウトルートへのアクセス

**トリガー**: `/ghost/signout`へのルート遷移

**処理フロー**:
1. `AuthenticatedRoute.beforeModel`で認証チェック（未認証ならサインイン画面へ）
2. `SignoutRoute.afterModel`が実行される
3. `notifications.clearAll()`で全ての通知をクリア
4. `session.invalidate()`でセッションを無効化
5. ember-simple-authがサーバーに`DELETE /ghost/api/admin/session`を送信
6. バックエンドでセッション削除
7. サインイン画面へリダイレクト

**遷移先**:
- 常時: `/ghost/signin` (サインイン画面)

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| サインアウト | sessions | DELETE/UPDATE | セッションレコードの削除またはuser_id無効化 |

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

#### sessions

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| DELETE/UPDATE | user_id | undefined/削除 | セッションからユーザー情報を削除 |
| UPDATE | verified | undefined | 2FA必須時、検証状態をクリア |

## メッセージ仕様

本機能は画面を持たず、サインアウト処理中にメッセージは表示されない。

## 例外処理

| 例外ケース | 対応処理 | 表示内容 |
|-----------|----------|----------|
| 未認証状態でアクセス | サインイン画面へリダイレクト | - |
| セッション削除失敗 | エラーログ出力、ローカルセッションは無効化 | - |

## 備考

- サインアウト処理は`ember-simple-auth`ライブラリの`session.invalidate()`を使用
- サーバーへのDELETE /sessionリクエストが失敗しても、クライアント側のセッションは無効化される
- 401エラーによるセッション無効化時は、無限ループ防止のためサーバーへのDELETEリクエストをスキップする（ajax.skipSessionDeletion）
- 全ての通知がクリアされるため、サインアウト前に表示されていたメッセージは失われる

---

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

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

### 推奨読解順序

#### Step 1: ルートの処理を理解する

サインアウトルートの実装を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | signout.js | `ghost/admin/app/routes/signout.js` | afterModelでの通知クリアとセッション無効化 |
| 1-2 | authenticated.js | `ghost/admin/app/routes/authenticated.js` | beforeModelでの認証チェック |

**主要処理フロー**:
- **signout.js 7-10行目**: afterModelで通知クリア、セッション無効化
- **authenticated.js 10-18行目**: 未認証時のリダイレクト処理

#### Step 2: セッション無効化処理を理解する

ember-simple-authのinvalidate処理を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | cookie.js | `ghost/admin/app/authenticators/cookie.js` | invalidate関数（51-61行目）でDELETE /sessionを送信 |

**主要処理フロー**:
- **51-61行目**: skipSessionDeletionフラグでDELETEスキップ判定、ajax.del(sessionEndpoint)実行

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

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | session.js | `ghost/core/core/server/api/endpoints/session.js` | delete関数（64-68行目）でログアウトミドルウェア呼び出し |
| 3-2 | middleware.js | `ghost/core/core/server/services/auth/session/middleware.js` | logout関数（29-40行目）でセッション削除 |
| 3-3 | session-service.js | `ghost/core/core/server/services/auth/session/session-service.js` | removeUserForSession（320-328行目）でuser_id削除 |

**主要処理フロー**:
- **session.js 64-68行目**: auth.session.logoutミドルウェアを返す
- **middleware.js 29-40行目**: sessionService.removeUserForSessionを呼び出し、204レスポンス
- **session-service.js 320-328行目**: user_idとverifiedをundefinedに設定

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

```
SignoutRoute.afterModel
    │
    ├─ notifications.clearAll()
    │
    └─ session.invalidate()
           └─ CookieAuthenticator.invalidate
                  └─ DELETE /ghost/api/admin/session
                         └─ SessionController.delete
                                └─ SessionMiddleware.logout
                                       └─ sessionService.removeUserForSession
                                              └─ session.user_id = undefined
                                              └─ session.verified = undefined
```

### データフロー図

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

/ghost/signout ────────▶ SignoutRoute.afterModel ─────────▶ サインイン画面へ
アクセス                       │                              リダイレクト
                              │
                              ▼
                    notifications.clearAll
                              │
                              ▼
                    session.invalidate
                              │
                              ▼
                    DELETE /session
                              │
                              ▼
                    sessions テーブル
                    (user_id = undefined)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| signout.js | `ghost/admin/app/routes/signout.js` | ルート | サインアウト処理の起点 |
| authenticated.js | `ghost/admin/app/routes/authenticated.js` | ルート（親） | 認証済みルートの基底クラス |
| cookie.js | `ghost/admin/app/authenticators/cookie.js` | 認証 | セッション無効化API呼び出し |
| session.js | `ghost/core/core/server/api/endpoints/session.js` | API | セッション管理エンドポイント |
| middleware.js | `ghost/core/core/server/services/auth/session/middleware.js` | ミドルウェア | ログアウト処理 |
| session-service.js | `ghost/core/core/server/services/auth/session/session-service.js` | サービス | セッションサービス実装 |
