# 画面設計書 201-アイデンティティ追加

## 概要

本ドキュメントは、GitLabの外部アイデンティティプロバイダ（OAuth、LDAP等）とユーザーアカウントを連携するための認可画面について定義する。

### 本画面の処理概要

本画面は、外部認証プロバイダ（OAuth2、LDAP等）を通じてサインインした際に、そのプロバイダとGitLabユーザーアカウントを紐付けるための認可確認画面である。

**業務上の目的・背景**：企業環境では、複数の認証プロバイダ（Google、GitHub、LDAP等）を利用してシングルサインオン（SSO）を実現することが一般的である。本画面は、ユーザーが新しい認証プロバイダを既存のGitLabアカウントに紐付ける際のセキュリティ確認を行い、不正なアカウント連携を防止する。ユーザーは明示的に認可を行うことで、そのプロバイダでのサインインが可能になる。

**画面へのアクセス方法**：本画面には直接アクセスすることはできない。外部認証プロバイダ経由でサインインを試みた際、そのプロバイダとの連携が未設定の場合に自動的にリダイレクトされる。アクセスにはセッション内にidentity_link_state、identity_link_provider、identity_link_extern_uidが設定されている必要がある。

**主要な操作・処理内容**：
1. 外部認証プロバイダの情報とユーザーアカウント情報の確認
2. 「Authorize」ボタンによる認可の承認
3. 「Cancel」ボタンによるキャンセル（アカウント設定画面へ遷移）

**画面遷移**：
- 遷移元：外部認証プロバイダからのOAuthコールバック
- 遷移先（認可時）：プロフィール/アカウント設定画面（成功メッセージ付き）
- 遷移先（キャンセル時）：プロフィール/アカウント設定画面
- 既に連携済みの場合：プロフィール/アカウント設定画面（通知メッセージ付き）

**権限による表示制御**：ログイン済みユーザーのみアクセス可能。セッション変数（state、provider、extern_uid）が正しく設定されていない場合は403エラーまたはアカウント設定画面へリダイレクトされる。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 89 | OAuth2クライアント | 主機能 | 外部アイデンティティの追加・認可処理 |
| 88 | OAuth2プロバイダ | 補助機能 | 認証プロバイダのラベル取得 |
| 76 | ユーザー登録 | 補助機能 | ユーザー情報の表示 |

## 画面種別

登録（認可確認）

## URL/ルーティング

- **URL**: `/-/user_settings/identities/new`
- **HTTPメソッド**: GET（表示）、POST（登録）
- **ルーティング**: `user_settings/identities#new`、`user_settings/identities#create`

## 入出力項目

| 項目名 | 項目種別 | データ型 | 必須 | 説明 |
|--------|----------|----------|------|------|
| state | URL パラメータ | String | 必須 | CSRF防止用のランダム文字列（セッションと照合） |
| provider | セッション | String | 必須 | 認証プロバイダ識別子（session[:identity_link_provider]） |
| extern_uid | セッション | String | 必須 | 外部ユーザー識別子（session[:identity_link_extern_uid]） |

## 表示項目

| 項目名 | 表示内容 | データソース |
|--------|----------|--------------|
| プロバイダ名 | 認証プロバイダの表示名 | Gitlab::Auth::OAuth::Provider.label_for(@identity.provider) |
| ユーザー名 | 現在ログイン中のユーザー名 | current_user.username |
| メールアドレス | 現在ログイン中のユーザーのメール | current_user.email |

## イベント仕様

### 1-Authorizeボタン押下

**処理内容**：
1. POSTリクエストを`/-/user_settings/identities`に送信
2. コントローラーで新規Identityレコードを作成
3. 保存成功時：「Authentication method updated」メッセージを設定
4. 保存失敗時：エラーメッセージを設定
5. セッション変数（identity_link_state、identity_link_provider、identity_link_extern_uid）を削除
6. プロフィール/アカウント設定画面にリダイレクト

### 2-Cancelボタン押下

**処理内容**：
1. `profile_account_path`へ遷移
2. 認可処理は行わない

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| Authorizeボタン押下 | identities | INSERT | 外部アイデンティティの登録 |

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

#### identities

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | user_id | current_user.id | 現在のログインユーザーID |
| INSERT | provider | session[:identity_link_provider] | 認証プロバイダ識別子 |
| INSERT | extern_uid | session[:identity_link_extern_uid] | 外部ユーザーID（正規化処理あり） |
| INSERT | created_at | 現在時刻 | 自動設定 |
| INSERT | updated_at | 現在時刻 | 自動設定 |

## メッセージ仕様

| 種別 | メッセージ | 表示条件 |
|------|----------|----------|
| 成功 | Authentication method updated | Identity登録成功時 |
| 通知 | Identity already exists | 既に同一のIdentityが存在する場合 |
| エラー | Error linking identity: %{errors} | Identity保存時にバリデーションエラーが発生した場合 |
| エラー | Error linking identity: Provider and Extern UID must be in the session. | セッション変数が不足している場合 |

## 例外処理

| 例外条件 | 処理内容 |
|----------|----------|
| state不一致 | 403 Forbiddenを返却（verify_stateで検証） |
| セッション変数不足 | アカウント設定画面にリダイレクト、エラーメッセージ表示 |
| Identity保存失敗 | アカウント設定画面にリダイレクト、エラーメッセージ表示 |

## 備考

- 本画面はDeviseレイアウト（layout: 'devise'）を使用する
- セキュリティ上、stateパラメータによるCSRF対策が必須
- extern_uidはプロバイダ種別に応じて正規化される（LDAPの場合はDN形式）

---

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

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

### 推奨読解順序

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

まず、Identityモデルの構造と外部認証プロバイダとの関係を理解することが重要。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | identity.rb | `app/models/identity.rb` | Identityモデルの構造、バリデーション、スコープを確認 |
| 1-2 | provider.rb | `lib/gitlab/auth/oauth/provider.rb` | OAuth認証プロバイダの判定ロジックを確認 |

**読解のコツ**: Identityモデルは`provider`と`extern_uid`の組み合わせでユーザーを一意に識別する。extern_uidの正規化処理（特にLDAP）に注意。

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

コントローラーの処理フローを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | identities_controller.rb | `app/controllers/user_settings/identities_controller.rb` | new/createアクションの処理フロー |

**主要処理フロー**:
1. **7行目**: verify_state - stateパラメータの検証
2. **8行目**: assign_variables_from_session - セッションからプロバイダ情報取得
3. **9行目**: verify_session_variables - セッション変数の存在確認
4. **11-24行目**: new - 表示処理、既存Identity確認
5. **26-36行目**: create - Identity登録処理

#### Step 3: ビューレイヤーを理解する

画面表示のロジックを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | new.html.haml | `app/views/user_settings/identities/new.html.haml` | 認可確認画面のUI構成 |

**主要処理フロー**:
- **1行目**: ページタイトル設定
- **2行目**: プロバイダラベルの取得
- **8-12行目**: 警告メッセージの表示
- **14-18行目**: フォーム（Authorize/Cancelボタン）

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

```
OAuthコールバック
    │
    ├─ IdentitiesController#new
    │      ├─ verify_state（before_action）
    │      ├─ assign_variables_from_session（before_action）
    │      ├─ verify_session_variables（before_action）
    │      └─ Identity.with_extern_uid.first_or_initialize
    │
    └─ IdentitiesController#create
           ├─ Identity.new
           ├─ identity.save
           ├─ delete_session_variables
           └─ redirect_to profile_account_path
```

### データフロー図

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

セッション変数 ───▶ IdentitiesController ───▶ Identity（DB）
(provider,                    │
 extern_uid,                  │
 state)                       ▼
                        new.html.haml ───▶ 認可確認画面
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| identities_controller.rb | `app/controllers/user_settings/identities_controller.rb` | コントローラー | アイデンティティ追加の処理制御 |
| identity.rb | `app/models/identity.rb` | モデル | アイデンティティデータの永続化 |
| new.html.haml | `app/views/user_settings/identities/new.html.haml` | ビュー | 認可確認画面のレンダリング |
| provider.rb | `lib/gitlab/auth/oauth/provider.rb` | ライブラリ | OAuthプロバイダのラベル取得 |
| user_settings.rb | `config/routes/user_settings.rb` | ルーティング | URLルーティング定義 |
