# 機能設計書 138-TikTok認可

## 概要

本ドキュメントは、Chatwootシステムにおける「TikTok認可」機能の設計仕様を記載する。本機能は、TikTok DMチャネルを設定するためのOAuth 2.0認証フローを実装する。

### 本機能の処理概要

TikTok認可機能は、企業がTikTokダイレクトメッセージ（DM）を通じた顧客対応をChatwootで管理するための認証基盤を提供する。TikTok Business API（OAuth 2.0）を使用してTikTokビジネスアカウントへのアクセス権限を取得し、DMの送受信を可能にする。

**業務上の目的・背景**：
TikTokは急成長するショートビデオプラットフォームであり、特に若年層をターゲットとするビジネスにとって重要な顧客接点となっている。Chatwootとの連携により、TikTokのDMを他のチャネル（チャット、メール等）と統合的に管理できるようになる。これにより、マルチプラットフォームでの効率的な顧客対応を実現する。

**機能の利用シーン**：
- 新規にTikTok DMチャネルを追加する場合
- 既存のTikTok連携を再認可する場合（トークン期限切れ時）
- TikTokビジネスアカウントからのダイレクトメッセージを受信・返信する場合

**主要な処理内容**：
1. TikTok OAuth 2.0認可URL生成とユーザーリダイレクト
2. 認可コードからアクセストークン・リフレッシュトークンへの変換
3. 必要なスコープの承認確認
4. TikTokビジネスアカウント情報の取得
5. TikTokチャネルの作成または更新
6. アバター設定
7. 既存連携の再認可処理

**関連システム・外部連携**：
- TikTok Business API
- oauth2 gem（OAuth 2.0クライアント）
- HTTParty（API呼び出し）
- TikTok Webhooks（DM受信）

**権限による制御**：
- TikTok認可を実行するにはAdministrator権限が必要
- 認可完了後のインボックス操作はエージェント権限でも可能

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 48 | インボックス新規作成画面 | 主画面 | 新しいTikTokチャネルの作成開始 |
| 49 | チャネル設定画面 | 主画面 | TikTok認可フローの開始 |
| 50 | インボックス設定画面 | 参照画面 | 認可完了後の設定画面 |

## 機能種別

OAuth認証 / 外部API連携 / データ作成・更新

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| code | String | Yes | TikTok認可コード | 有効な認可コード |
| state | String | Yes | CSRF対策用state（暗号化トークン） | 有効なトークン |

### 入力データソース

- TikTok OAuth認証コールバック
- 管理画面からの認可開始リクエスト

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| redirect_url | String | TikTok認可画面へのリダイレクトURL |
| inbox_id | Integer | 作成/更新されたインボックスID |
| tiktok_channel | Object | 作成/更新されたTikTokチャネル情報 |

### 出力先

- データベース（channel_tiktok, inboxes テーブル）
- 管理画面へのリダイレクト
- API レスポンス（JSON形式）

## 処理フロー

### 処理シーケンス

```
1. 認可開始リクエスト（AuthorizationsController#create）
   └─ 管理者権限チェック（基底クラス）
   └─ stateトークン生成（generate_tiktok_token）
   └─ TikTok OAuth認可URL生成（Tiktok::AuthClient.authorize_url）

2. TikTokログイン・権限承認
   └─ ユーザーがTikTokでログイン
   └─ 必要な権限（REQUIRED_SCOPES）を承認
   └─ 認可コードを発行してコールバック

3. コールバック処理（CallbacksController#show）
   └─ エラーチェック（ユーザーキャンセル等）
   └─ スコープ承認確認（all_scopes_granted?）
   └─ stateトークン検証（verify_tiktok_token）
   └─ 短期アクセストークン取得

4. ビジネスアカウント情報取得
   └─ Tiktok::Client.business_account_details

5. チャネル作成/更新
   └─ business_idで既存チャネル検索
   └─ 存在しない場合: 新規チャネル・インボックス作成
   └─ 存在する場合: トークン情報更新

6. 再認可・アバター設定
   └─ reauthorized!呼び出し
   └─ プロフィール画像でアバター設定

7. リダイレクト
   └─ 新規: エージェント追加画面へ
   └─ 更新: インボックス設定画面へ
```

### フローチャート

```mermaid
flowchart TD
    A[開始: TikTok連携開始] --> B[管理者権限チェック]
    B --> C{権限あり?}
    C -->|No| D[403エラー]
    C -->|Yes| E[state生成]
    E --> F[TikTok認可URL生成]
    F --> G[JSONレスポンス返却]
    G --> H[フロントエンドがリダイレクト]
    H --> I[TikTokログイン画面]
    I --> J{認証成功?}
    J -->|No| K[エラーリダイレクト]
    J -->|Yes| L[コールバック受信]
    L --> M{エラーパラメータ?}
    M -->|Yes| N[handle_authorization_error]
    M -->|No| O{全スコープ承認?}
    O -->|No| P[handle_ungranted_scopes_error]
    O -->|Yes| Q[短期トークン取得]
    Q --> R[ビジネス情報取得]
    R --> S{既存チャネルあり?}
    S -->|Yes| T[チャネル更新]
    S -->|No| U[新規チャネル作成]
    U --> V[インボックス作成]
    T --> W[reauthorized!]
    V --> W
    W --> X[アバター設定]
    X --> Y[リダイレクト]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-138-01 | 管理者権限必須 | TikTok認可はAdministrator権限が必要 | 認可開始時 |
| BR-138-02 | 全スコープ必須 | REQUIRED_SCOPES全てが承認されている必要がある | コールバック時 |
| BR-138-03 | リフレッシュトークン保存 | アクセストークンとリフレッシュトークン両方を保存 | トークン取得後 |
| BR-138-04 | business_id一意 | business_idでチャネルを一意に識別 | チャネル検索時 |
| BR-138-05 | トークン有効期限管理 | expires_atとrefresh_token_expires_atの両方を保存 | トークン保存時 |

### 計算ロジック

トークン有効期限の計算:
```ruby
expires_at: Time.current + json['data']['expires_in'].seconds
refresh_token_expires_at: Time.current + json['data']['refresh_token_expires_in'].seconds
```

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| チャネル作成 | channel_tiktok | INSERT | 新規TikTokチャネル作成 |
| インボックス作成 | inboxes | INSERT | インボックス作成とチャネル関連付け |
| チャネル更新 | channel_tiktok | UPDATE | トークン情報の更新 |
| インボックス更新 | inboxes | UPDATE | インボックス名の更新 |

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

#### channel_tiktok

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT/UPDATE | business_id | short_term_access_token[:business_id] | TikTokビジネスID |
| INSERT/UPDATE | access_token | short_term_access_token[:access_token] | アクセストークン |
| INSERT/UPDATE | refresh_token | short_term_access_token[:refresh_token] | リフレッシュトークン |
| INSERT/UPDATE | expires_at | short_term_access_token[:expires_at] | トークン有効期限 |
| INSERT/UPDATE | refresh_token_expires_at | short_term_access_token[:refresh_token_expires_at] | リフレッシュトークン有効期限 |
| INSERT | account_id | account.id | アカウントID |

#### inboxes

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT/UPDATE | name | business_details[:display_name] or username | 表示名またはユーザー名 |
| INSERT | channel_type | 'Channel::Tiktok' | チャネルタイプ |
| INSERT | channel_id | 作成されたチャネルID | ポリモーフィック関連 |
| INSERT | account_id | account.id | アカウントID |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 403 | Forbidden | Administrator権限なし | 管理者でログインし直す |
| 400 | access_denied | 認可拒否・キャンセル | 再度認可フローを開始 |
| 400 | ungranted_scopes | 必要なスコープが未承認 | 全スコープを承認して再認可 |
| 500 | Internal Error | トークン交換失敗 | 再度認可フローを開始 |

### リトライ仕様

- トークン交換失敗時、例外をキャプチャしてエラーページにリダイレクト

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

- 新規チャネル作成：`ActiveRecord::Base.transaction`内でチャネルとインボックスを作成

## パフォーマンス要件

- TikTok Business API呼び出しは外部依存のため、タイムアウトを適切に設定
- 認可URLの生成は即座に完了

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

- TIKTOK_APP_IDとTIKTOK_APP_SECRETは環境変数/GlobalConfigServiceで管理
- stateトークンによるCSRF対策
- アクセストークンとリフレッシュトークンは暗号化して保存

## 備考

- TikTok Business API（OAuth 2.0）を使用
- REQUIRED_SCOPESには多数の権限（user.info, message等）が含まれる
- Webhookによるリアルタイムメッセージ受信に対応

---

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

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

### 推奨読解順序

#### Step 1: TikTok認証クライアントを理解する

TikTok API呼び出しの中核を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | auth_client.rb | `app/services/tiktok/auth_client.rb` | TikTok認証の全体構造 |

**主要処理フロー**:
- **2-3行目**: REQUIRED_SCOPES - 必要な権限スコープ一覧
- **6-26行目**: authorize_url - 認可URL生成（OAuth2::Clientを使用）
- **29-56行目**: obtain_short_term_access_token - 短期トークン取得
- **58-82行目**: renew_short_term_access_token - トークン更新
- **84-109行目**: webhook_callback/update_webhook_callback - Webhook管理

#### Step 2: 認可開始処理を理解する

認可URL生成のコントローラーを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | authorizations_controller.rb | `app/controllers/api/v1/accounts/tiktok/authorizations_controller.rb` | 認可URL生成 |

**主要処理フロー**:
- **4-14行目**: create アクション
  - Tiktok::AuthClient.authorize_url(state: ...)
  - generate_tiktok_token(account_id)でstate生成

#### Step 3: コールバック処理を理解する

認可コールバックの処理を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | callbacks_controller.rb | `app/controllers/tiktok/callbacks_controller.rb` | TikTokコールバック処理 |

**主要処理フロー**:
- **4-11行目**: show アクション - エラーチェックと処理分岐
- **15-17行目**: all_scopes_granted? - スコープ承認確認
- **20-27行目**: process_successful_authorization - 成功時処理
- **67-84行目**: find_or_create_inbox - チャネル作成/更新
- **86-105行目**: create_channel_with_inbox - 新規作成
- **134-136行目**: short_term_access_token - トークン取得

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

```
Api::V1::Accounts::Tiktok::AuthorizationsController#create
    |
    +-- check_authorization (from OauthAuthorizationController)
    |
    +-- Tiktok::AuthClient.authorize_url(state: ...)
    |       |
    |       +-- OAuth2::Client.new(client_id, client_secret)
    |       +-- client.authorize_url(response_type, client_key, redirect_uri, scope, state)
    |
    +-- generate_tiktok_token(Current.account.id)
    |
    +-- render json: { success: true, url: redirect_url }

Tiktok::CallbacksController#show
    |
    +-- error? チェック
    |       |
    |       +-- handle_authorization_error (エラー時)
    |
    +-- all_scopes_granted?
    |       |
    |       +-- handle_ungranted_scopes_error (スコープ不足時)
    |
    +-- process_successful_authorization
            |
            +-- short_term_access_token
            |       |
            |       +-- Tiktok::AuthClient.obtain_short_term_access_token(code)
            |
            +-- find_or_create_inbox
                    |
                    +-- tiktok_client.business_account_details
                    |
                    +-- find_channel (by business_id)
                    |
                    +-- update_channel or create_channel_with_inbox
                    |
                    +-- channel_tiktok.reauthorized!
                    |
                    +-- set_avatar (Avatar::AvatarFromUrlJob)
```

### データフロー図

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

管理者リクエスト      --> AuthorizationsController#create --> 認可URL
(認可開始)               |                                  |
                         +-- AuthClient.authorize_url       +-- { success, url }
                         +-- generate_tiktok_token

TikTok OAuth         --> CallbacksController#show        --> チャネル・インボックス
(code, state)            |                                  |
                         +-- all_scopes_granted?            +-- Channel::Tiktok
                         +-- obtain_short_term_access_token +-- Inbox
                         +-- business_account_details       +-- トークン情報
                         +-- find_or_create_inbox           +-- アバター
                         +-- reauthorized!
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| auth_client.rb | `app/services/tiktok/auth_client.rb` | サービス | TikTok認証クライアント |
| authorizations_controller.rb | `app/controllers/api/v1/accounts/tiktok/authorizations_controller.rb` | コントローラー | 認可URL生成 |
| callbacks_controller.rb | `app/controllers/tiktok/callbacks_controller.rb` | コントローラー | TikTokコールバック |
| tiktok.rb | `app/models/channel/tiktok.rb` | モデル | TikTokチャネルのデータモデル |
| integration_helper.rb | `app/helpers/tiktok/integration_helper.rb` | ヘルパー | stateトークン生成・検証 |
| client.rb | `app/services/tiktok/client.rb` | サービス | TikTok APIクライアント |
