# 機能設計書 6-SSO認証

## 概要

本ドキュメントは、ChatwootシステムにおけるSSO（シングルサインオン）認証機能の設計を記載する。SSOトークンを使用した自動ログイン機能を実現する。

### 本機能の処理概要

本機能は、外部認証プロバイダー（OmniAuth、SAML）からの認証成功後にSSOトークンを生成し、そのトークンを使用した自動ログインを可能にする機能を提供する。

**業務上の目的・背景**：エンタープライズ環境では、組織の既存IDプロバイダー（IdP）を使用したシングルサインオンが必須となる。本機能により、ユーザーは一度の認証で複数システムにアクセスでき、認証情報の再入力を省略することで業務効率を向上させる。

**機能の利用シーン**：
- OmniAuth認証成功後のログイン画面への自動リダイレクト
- SAML IdPからのコールバック後の自動ログイン
- Super Admin画面からのユーザーなりすまし（Impersonation）

**主要な処理内容**：
1. SSOトークンの生成（64文字の16進数）
2. Redisへのトークン一時保存（5分間有効）
3. SSOトークンを含むログインURLの生成
4. SSOトークンの検証とログイン処理
5. 使用後のトークン無効化

**関連システム・外部連携**：
- Redis（SSOトークン一時保存）
- OmniAuth（OAuth2/SAML認証）
- Super Admin（Impersonation機能）

**権限による制御**：SSOトークン生成はOmniAuth認証成功後またはSuper Admin権限で可能。Impersonation機能はSuper Adminのみ使用可能。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 2 | SSOログイン画面 | 主画面 | SAML認証によるシングルサインオンログイン |
| 1 | ログイン画面 | 参照画面 | SSOトークンによる自動ログイン |

## 機能種別

認証連携 / シングルサインオン

## 入力仕様

### 入力パラメータ

#### SSOログイン

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| email | String | Yes | ユーザーメールアドレス | 有効なメール形式 |
| sso_auth_token | String | Yes | SSOトークン | 64文字16進数、5分間有効 |

### 入力データソース

- URLパラメータ（リダイレクト時）
- Redis（トークン検証）

## 出力仕様

### 出力データ

#### SSOトークン生成

| 項目名 | 型 | 説明 |
|--------|-----|------|
| sso_auth_token | String | 64文字の16進数トークン |
| sso_link | String | SSOトークンを含むログインURL |

#### SSOログイン成功

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

### 出力先

- Redis（SSOトークン保存）
- HTTPレスポンスヘッダー（認証トークン）
- HTTPレスポンスボディ（JSON形式）

## 処理フロー

### 処理シーケンス

```
【SSOトークン生成フロー】
1. 外部認証成功
   └─ OmniAuthコールバック受信
   └─ または Super Admin からの Impersonation

2. SSOトークン生成
   └─ SecureRandom.hex(32) で64文字トークン生成
   └─ Redis に user_id とトークンをキーとして保存
   └─ TTL 5分で自動失効設定

3. SSOリンク生成
   └─ FRONTEND_URL + /app/login?email=xxx&sso_auth_token=xxx
   └─ Impersonation時は impersonation=true パラメータ追加

【SSOログインフロー】
4. SSOリンクアクセス
   └─ SessionsController#create の before_action
   └─ process_sso_auth_token 実行

5. トークン検証
   └─ User.from_email でユーザー取得
   └─ user.valid_sso_auth_token? で Redis 検証

6. 認証実行
   └─ @resource にユーザー設定
   └─ authenticate_resource_with_sso_token 実行
   └─ create_token で認証トークン生成
   └─ sign_in でセッション確立

7. トークン無効化
   └─ invalidate_sso_auth_token で Redis からトークン削除
```

### フローチャート

```mermaid
flowchart TD
    A[外部認証成功] --> B[generate_sso_auth_token]
    B --> C[SecureRandom.hex 32]
    C --> D[Redis.setex 5分]
    D --> E[generate_sso_link]
    E --> F[ログイン画面へリダイレクト]

    G[SSOリンクアクセス] --> H[process_sso_auth_token]
    H --> I[User.from_email]
    I --> J{ユーザー存在?}
    J -->|No| K[通常ログインフローへ]
    J -->|Yes| L[valid_sso_auth_token?]
    L --> M{トークン有効?}
    M -->|No| K
    M -->|Yes| N[@resource = user]
    N --> O[handle_sso_authentication]
    O --> P[create_token]
    P --> Q[sign_in]
    Q --> R[invalidate_sso_auth_token]
    R --> S[200 OK + 認証情報]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-001 | トークン有効期限 | SSOトークンは5分間のみ有効 | トークン生成時 |
| BR-002 | 一度限りの使用 | SSOトークンは使用後即座に無効化 | ログイン成功時 |
| BR-003 | ユーザー紐付け | SSOトークンはuser_idと紐付けて保存 | トークン生成時 |
| BR-004 | Impersonationフラグ | Super Adminからのなりすまし時はフラグ付与 | Impersonation時 |

### 計算ロジック

- **SSOトークン生成**: `SecureRandom.hex(32)` で64文字の16進数
- **Redisキー**: `USER_SSO_AUTH_TOKEN:{user_id}:{token}` 形式

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| ユーザー検索 | users | SELECT | email条件でユーザー取得 |
| トークン保存 | users | UPDATE | DeviseTokenAuthトークン更新 |

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

#### users

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | * | WHERE email = :email | SSOログイン時 |
| UPDATE | tokens | 認証トークン情報 | ログイン成功時 |

#### Redis

| 操作 | キー | 値 | TTL |
|-----|------|-----|-----|
| SETEX | USER_SSO_AUTH_TOKEN:{user_id}:{token} | true | 5分 |
| GET | USER_SSO_AUTH_TOKEN:{user_id}:{token} | true/nil | - |
| DEL | USER_SSO_AUTH_TOKEN:{user_id}:{token} | - | - |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| N/A | フォールバック | SSOトークン無効またはユーザー不存在 | 通常ログインフローへ |

### リトライ仕様

- SSOトークンが無効な場合、通常のログインフローにフォールバック
- 新しいSSOリンクの生成が必要

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

- SSOトークンの生成・保存はRedis操作（トランザクション不要）
- 認証トークン生成・保存は単一トランザクション

## パフォーマンス要件

- SSOトークン生成: 50ms以内
- Redis検証: 10ms以内
- ログイン処理全体: 500ms以内

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

- SSOトークンは64文字の暗号学的乱数（SecureRandom.hex(32)）
- トークンは5分間のみ有効（Redis TTL）
- 使用後は即座に無効化
- Impersonation機能はSuper Admin権限のみ
- トークンはuser_idと紐付けて保存（他ユーザーへの流用防止）

## 備考

- RedisキーのフォーマットはRedis::RedisKeysで定義
- FRONTEND_URL環境変数でリダイレクト先を制御
- Mobile Deep Linkにも対応（MOBILE_DEEP_LINK_BASE設定）

---

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

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

### 推奨読解順序

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

SSOトークンの保存形式とRedisキー構造を把握。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | sso_authenticatable.rb | `app/models/concerns/sso_authenticatable.rb` | SSOトークン管理のインターフェース |

**読解のコツ**:
- 4-8行目: generate_sso_auth_tokenでトークン生成とRedis保存
- 10-12行目: invalidate_sso_auth_tokenでトークン無効化
- 14-16行目: valid_sso_auth_token?でトークン検証
- 29-31行目: sso_token_keyでRedisキー生成

#### Step 2: トークン生成フローを理解する

OmniAuth認証成功後のSSOトークン生成を把握。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | omniauth_callbacks_controller.rb | `app/controllers/devise_overrides/omniauth_callbacks_controller.rb` | sign_in_user での SSOトークン使用 |

**主要処理フロー**:
1. **12-19行目**: sign_in_userでSSOトークン生成
2. **19行目**: generate_sso_auth_tokenでトークン生成
3. **19行目**: リダイレクトURLにsso_auth_tokenパラメータ付与

#### Step 3: SSOログインフローを理解する

SessionsControllerでのSSOトークン検証とログイン処理を把握。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | sessions_controller.rb | `app/controllers/devise_overrides/sessions_controller.rb` | SSOトークンによるログイン |

**主要処理フロー**:
1. **5行目**: before_action :process_sso_auth_token
2. **13行目**: handle_sso_authentication の呼び出し条件
3. **47-51行目**: handle_sso_authenticationで認証実行
4. **59-66行目**: authenticate_resource_with_sso_tokenでトークン生成・ログイン
5. **68-73行目**: process_sso_auth_tokenでトークン検証

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

```
【トークン生成】
User#generate_sso_auth_token (SsoAuthenticatable)
    │
    ├─ SecureRandom.hex(32)
    │      └─ 64文字の16進数トークン生成
    │
    ├─ sso_token_key(token)
    │      └─ Redis::RedisKeys::USER_SSO_AUTH_TOKEN
    │
    └─ Redis::Alfred.setex(key, true, 5.minutes)

【トークン検証・ログイン】
DeviseOverrides::SessionsController#create
    │
    ├─ process_sso_auth_token (before_action)
    │      ├─ User.from_email(params[:email])
    │      └─ user.valid_sso_auth_token?(params[:sso_auth_token])
    │             └─ Redis::Alfred.get(sso_token_key(token))
    │
    └─ handle_sso_authentication
           └─ authenticate_resource_with_sso_token
                  ├─ @resource.create_token
                  ├─ @resource.save!
                  ├─ sign_in(:user, @resource)
                  └─ @resource.invalidate_sso_auth_token
                         └─ Redis::Alfred.delete(key)
```

### データフロー図

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

【トークン生成】
OmniAuth認証成功 ─────▶ generate_sso_auth_token
                              │
                              ▼
                       SecureRandom.hex(32) ──────────▶ sso_auth_token
                              │
                              ▼
                       Redis.setex ───────────────────▶ Redis (5分TTL)
                              │
                              ▼
                       generate_sso_link ─────────────▶ リダイレクトURL

【SSOログイン】
email + sso_auth_token ▶ process_sso_auth_token
                              │
                              ▼
                       User.from_email ───────────────▶ @resource
                              │
                              ▼
                       valid_sso_auth_token? ─────────▶ Redis検証
                              │
                              ▼
                       authenticate_resource_with_sso_token
                              │
                              ├─ create_token ────────▶ HTTPヘッダー
                              │
                              └─ invalidate_sso_auth_token
                                     │
                                     ▼
                              Redis.delete ───────────▶ トークン無効化
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| sso_authenticatable.rb | `app/models/concerns/sso_authenticatable.rb` | concern | SSOトークン管理 |
| sessions_controller.rb | `app/controllers/devise_overrides/sessions_controller.rb` | コントローラー | SSOログイン処理 |
| omniauth_callbacks_controller.rb | `app/controllers/devise_overrides/omniauth_callbacks_controller.rb` | コントローラー | SSOトークン生成トリガー |
| redis_keys.rb | `lib/redis/redis_keys.rb` | ライブラリ | Redisキー定義 |
| alfred.rb | `lib/redis/alfred.rb` | ライブラリ | Redis操作ヘルパー |
| user.rb | `app/models/user.rb` | モデル | SsoAuthenticatable include |
