# 機能設計書 135-Microsoft認可

## 概要

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

### 本機能の処理概要

Microsoft認可機能は、企業がMicrosoft 365またはOutlook.comのメールアカウントを通じたカスタマーサポートをChatwootで管理するための認証基盤を提供する。Azure AD/Microsoft Identity Platformを使用してMicrosoftアカウントへのアクセス権限を取得し、IMAP/SMTPによるメール送受信を可能にする。

**業務上の目的・背景**：
Microsoft 365は企業向けの主要なメール・グループウェアプラットフォームであり、多くの企業がOutlookをビジネスメールとして使用している。Chatwootとの連携により、Microsoft 365メールによる顧客問い合わせを他のチャネル（チャット、SNS等）と統合的に管理できるようになる。これにより、チームでの効率的なメール対応と、顧客とのコミュニケーション履歴の一元管理を実現する。

**機能の利用シーン**：
- 新規にMicrosoft 365/Outlookメールチャネルを追加する場合
- 既存のMicrosoft連携を再認可する場合（トークン期限切れ時）
- 組織のAzure AD管理下のメールアカウントを連携する場合

**主要な処理内容**：
1. Microsoft OAuth 2.0認可URL生成とユーザーリダイレクト
2. 認可コードからアクセストークン・リフレッシュトークンへの変換
3. Microsoftユーザー情報（メールアドレス、名前）の取得
4. Emailチャネルの作成または更新
5. IMAP設定の自動構成（Outlook.office365.com）
6. 既存連携の再認可処理

**関連システム・外部連携**：
- Microsoft Identity Platform（login.microsoftonline.com）
- Microsoft Graph API
- oauth2 gem（OAuth 2.0クライアント）
- Outlook IMAP/SMTP サーバー（outlook.office365.com）

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

## 関連画面

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

## 機能種別

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

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| code | String | Yes | Microsoft認可コード | 有効な認可コード |
| state | String | Yes | CSRF対策用state（Signed Global ID） | 有効期限15分以内 |

### 入力データソース

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

## 出力仕様

### 出力データ

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

### 出力先

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

## 処理フロー

### 処理シーケンス

```
1. 認可開始リクエスト（AuthorizationsController#create）
   └─ 管理者権限チェック
   └─ stateパラメータ生成（Signed Global ID、有効期限15分）
   └─ Microsoft OAuth認可URL生成

2. Microsoftログイン・権限承認
   └─ ユーザーがMicrosoftでログイン
   └─ 必要な権限（IMAP、SMTP、プロフィール）を承認
   └─ 認可コードを発行してコールバック

3. コールバック処理（CallbacksController#show）
   └─ stateパラメータ検証
   └─ 認可コードでトークン交換
   └─ IDトークンからユーザー情報取得

4. チャネル作成/更新
   └─ 既存チャネル検索（メールアドレス）
   └─ 存在しない場合: 新規チャネル・インボックス作成
   └─ 存在する場合: トークン情報更新

5. IMAP設定
   └─ imap_address: outlook.office365.com
   └─ imap_port: 993
   └─ imap_enabled: true
   └─ provider: microsoft

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

### フローチャート

```mermaid
flowchart TD
    A[開始: Microsoft連携開始] --> B[管理者権限チェック]
    B --> C{権限あり?}
    C -->|No| D[403エラー]
    C -->|Yes| E[state生成]
    E --> F[Microsoft認可URL生成]
    F --> G[JSONレスポンス返却]
    G --> H[フロントエンドがリダイレクト]
    H --> I[Microsoftログイン画面]
    I --> J{認証成功?}
    J -->|No| K[エラーリダイレクト]
    J -->|Yes| L[コールバック受信]
    L --> M[state検証]
    M --> N{検証OK?}
    N -->|No| K
    N -->|Yes| O[トークン交換]
    O --> P[ユーザー情報取得]
    P --> Q{既存チャネルあり?}
    Q -->|Yes| R[チャネル更新]
    Q -->|No| S[新規チャネル作成]
    S --> T[インボックス作成]
    R --> U[reauthorized!呼び出し]
    T --> U
    U --> V[リダイレクト]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-135-01 | 管理者権限必須 | Microsoft認可はAdministrator権限が必要 | 認可開始時 |
| BR-135-02 | state有効期限 | stateパラメータは15分間有効 | コールバック時 |
| BR-135-03 | 同意画面強制表示 | prompt=consentで毎回同意画面を表示 | 認可リクエスト時 |
| BR-135-04 | オフラインアクセス | offline_accessスコープでリフレッシュトークン取得 | 認可リクエスト時 |
| BR-135-05 | commonエンドポイント | /common/を使用して任意のテナントに対応 | 認可URL生成時 |
| BR-135-06 | トークン有効期限 | アクセストークンは1時間後に期限切れ | トークン保存時 |

### 計算ロジック

トークン有効期限の計算:
```ruby
expires_on: (Time.current.utc + 1.hour).to_s
```

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| チャネル作成 | channel_email | INSERT | 新規Emailチャネル作成 |
| インボックス作成 | inboxes | INSERT | インボックス作成とチャネル関連付け |
| チャネル更新 | channel_email | UPDATE | トークン・IMAP設定の更新 |

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

#### channel_email

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT/UPDATE | email | users_data['email'] | Microsoftアカウントのメールアドレス |
| INSERT/UPDATE | imap_login | users_data['email'] | IMAPログイン用 |
| INSERT/UPDATE | imap_address | 'outlook.office365.com' | Outlook IMAPサーバー |
| INSERT/UPDATE | imap_port | '993' | IMAP SSL ポート |
| INSERT/UPDATE | imap_enabled | true | IMAP有効化 |
| INSERT/UPDATE | provider | 'microsoft' | プロバイダー識別子 |
| INSERT/UPDATE | provider_config | { access_token, refresh_token, expires_on } | OAuth トークン情報 |

#### inboxes

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | name | users_data['name'] or fallback_name | ユーザー名またはメールのローカル部分 |
| INSERT | channel_type | 'Channel::Email' | チャネルタイプ |
| INSERT | channel_id | 作成されたチャネルID | ポリモーフィック関連 |
| INSERT | account_id | account.id (from state) | アカウントID |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 403 | Forbidden | Administrator権限なし | 管理者でログインし直す |
| 400 | Bad Request | stateパラメータなし | 再度認可フローを開始 |
| 422 | Unprocessable Entity | state期限切れまたは無効 | 再度認可フローを開始 |
| 500 | Internal Server Error | トークン交換失敗 | Microsoftの状態確認、再試行 |

### リトライ仕様

- トークン交換失敗時、例外をキャプチャしてルートへリダイレクト

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

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

## パフォーマンス要件

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

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

- Signed Global IDによるCSRF対策（state パラメータ）
- AZURE_APP_IDとAZURE_APP_SECRETは環境変数で管理
- リフレッシュトークンは暗号化して保存
- アクセストークンは有効期限付き

## 備考

- oauth2 gemを使用したOAuth 2.0実装
- prompt=consentにより、リフレッシュトークンを確実に取得
- /common/エンドポイントを使用して、個人・組織アカウント両方に対応
- IMAP/SMTPスコープによりメール操作が可能

---

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

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

### 推奨読解順序

#### Step 1: Microsoft認証共通機能を理解する

Microsoft OAuth設定の共通処理を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | microsoft_concern.rb | `app/controllers/concerns/microsoft_concern.rb` | microsoft_clientの初期化、scopeの定義 |

**主要処理フロー**:
- **4-14行目**: microsoft_client - OAuth2::Clientの初期化
  - AZURE_APP_IDとAZURE_APP_SECRETをGlobalConfigServiceから取得
  - site: login.microsoftonline.com
  - authorize_url: /common/oauth2/v2.0/authorize
  - token_url: /common/oauth2/v2.0/token
- **18-20行目**: scope - 要求する権限スコープ
  - offline_access https://outlook.office.com/IMAP.AccessAsUser.All https://outlook.office.com/SMTP.Send openid profile email

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

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

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

**主要処理フロー**:
- **4-18行目**: create アクション - 認可URL生成
  - redirect_uri: {base_url}/microsoft/callback
  - scope: MicrosoftConcernから取得
  - state: Signed Global ID（15分有効）
  - prompt: consent（毎回同意画面表示）
- 基底クラス（10-12行目）: state生成
  - Current.account.to_sgid(expires_in: 15.minutes).to_s

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

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | callbacks_controller.rb | `app/controllers/microsoft/callbacks_controller.rb` | Microsoft固有のコールバック処理 |
| 3-2 | oauth_callback_controller.rb | `app/controllers/oauth_callback_controller.rb` | 共通コールバック基底クラス |

**主要処理フロー**:
- MicrosoftCallbacksController:
  - **6-8行目**: oauth_client - MicrosoftConcernのmicrosoft_clientを使用
  - **10-12行目**: provider_name - 'microsoft'を返す
  - **14-16行目**: imap_address - 'outlook.office365.com'を返す
- OauthCallbackController:
  - **2-12行目**: show アクション - トークン交換と処理
  - **16-23行目**: handle_response - インボックス作成/更新とリダイレクト
  - **41-43行目**: find_channel_by_email - 既存チャネル検索
  - **45-56行目**: update_channel - IMAP設定とトークン保存

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

```
Api::V1::Accounts::Microsoft::AuthorizationsController#create
    |
    +-- check_authorization (before_action)
    |       |
    |       +-- Current.account_user.administrator?
    |
    +-- microsoft_client.auth_code.authorize_url
    |       |
    |       +-- MicrosoftConcern#microsoft_client
    |       |       |
    |       |       +-- OAuth2::Client.new(AZURE_APP_ID, AZURE_APP_SECRET)
    |       |
    |       +-- state (from OauthAuthorizationController)
    |               |
    |               +-- Current.account.to_sgid(expires_in: 15.minutes)
    |
    +-- render json: { success: true, url: redirect_url }

Microsoft::CallbacksController#show
    |
    +-- oauth_client.auth_code.get_token(code, redirect_uri)
    |       |
    |       +-- microsoft_client (from MicrosoftConcern)
    |
    +-- handle_response (from OauthCallbackController)
            |
            +-- find_or_create_inbox
            |       |
            |       +-- find_channel_by_email
            |       |
            |       +-- create_channel_with_inbox (新規の場合)
            |               |
            |               +-- Channel::Email.create!(email: users_data['email'])
            |               +-- account.inboxes.create!(channel: channel_email)
            |
            +-- update_channel
            |       |
            |       +-- channel_email.update!({
            |               imap_login, imap_address: 'outlook.office365.com',
            |               imap_port: '993', imap_enabled: true,
            |               provider: 'microsoft',
            |               provider_config: { access_token, refresh_token, expires_on }
            |           })
            |
            +-- channel_email.reauthorized!
            |
            +-- redirect_to (agents or settings)
```

### データフロー図

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

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

Microsoft OAuth      --> CallbacksController#show        --> チャネル・インボックス
(code, state)            |                                  |
                         +-- get_token(code)                +-- Channel::Email
                         +-- users_data (IDトークン)        +-- Inbox
                         +-- find_or_create_inbox           +-- IMAP設定
                         +-- update_channel
                         +-- reauthorized!
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| microsoft_concern.rb | `app/controllers/concerns/microsoft_concern.rb` | Concern | Microsoft OAuth クライアント設定 |
| authorizations_controller.rb | `app/controllers/api/v1/accounts/microsoft/authorizations_controller.rb` | コントローラー | 認可URL生成 |
| oauth_authorization_controller.rb | `app/controllers/api/v1/accounts/oauth_authorization_controller.rb` | コントローラー | OAuth認可基底クラス |
| callbacks_controller.rb | `app/controllers/microsoft/callbacks_controller.rb` | コントローラー | Microsoft固有コールバック |
| oauth_callback_controller.rb | `app/controllers/oauth_callback_controller.rb` | コントローラー | 共通コールバック基底クラス |
| email.rb | `app/models/channel/email.rb` | モデル | Emailチャネルのデータモデル |
| global_config_service.rb | `app/services/global_config_service.rb` | サービス | 設定値取得 |
