# 画面設計書 10-デバイス認可画面

## 概要

本ドキュメントは、GitLabのデバイス認可画面の設計仕様を記載したものです。OAuth 2.0 Device Authorization Grant（RFC 8628）に基づき、入力が困難なデバイス（スマートTV、IoTデバイス等）からGitLabへのアクセスを認可するための画面です。

### 本画面の処理概要

**業務上の目的・背景**：デバイス認可画面は、キーボードやブラウザを持たないデバイス（CLIツール、スマートTV、ゲーム機、IoTデバイス等）がOAuthアクセストークンを取得するための認可フローを提供します。ユーザーはデバイスに表示されたユーザーコードを、別のデバイス（PC、スマートフォン）のブラウザでGitLabに入力し、認可を行います。これにより、入力制限のあるデバイスでも安全にGitLab APIへのアクセス権を取得できます。

**画面へのアクセス方法**：
- `/oauth/device` にアクセス（デバイスが表示する verification_uri）
- デバイスに表示されたユーザーコードを入力

**主要な操作・処理内容**：
1. ユーザーコードの入力
2. ユーザーコードの検証
3. 認可スコープの確認
4. 認可の承認
5. デバイスへのアクセストークン発行（ポーリング経由）

**画面遷移**：
- 遷移元：デバイスに表示されたURLからのアクセス
- 遷移先：認可完了画面、エラー画面

**権限による表示制御**：
- 認証済みユーザーのみがアクセス可能
- 管理者ユーザーには追加の警告メッセージが表示される

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 88 | OAuth2プロバイダ | 主機能 | デバイス認可フローの処理 |

## 画面種別

確認・認可画面（2段階フロー）

## URL/ルーティング

| HTTPメソッド | URL | アクション |
|-------------|-----|----------|
| GET | `/oauth/device` | ユーザーコード入力画面表示 |
| POST | `/oauth/device/confirm` | ユーザーコード検証・認可確認画面表示 |
| POST | `/oauth/device_authorizations/authorize` | 認可承認処理 |

## 入出力項目

### 入力項目（ユーザーコード入力画面）

| 項目名 | 物理名 | 型 | 必須 | 最大長 | 説明 |
|--------|--------|-----|------|--------|------|
| ユーザーコード | user_code | string | Yes | - | デバイスに表示されたコード |

### 送信項目（認可確認画面）

| 項目名 | 物理名 | 型 | 説明 |
|--------|--------|-----|------|
| ユーザーコード | user_code | string | 検証済みユーザーコード（隠しフィールド） |

## 表示項目

### ユーザーコード入力画面

| 項目名 | 説明 | 条件 |
|--------|------|------|
| ページタイトル | 「Authorize device to access to your GitLab account.」 | 常時表示 |
| ユーザーコード入力欄 | デバイスコード入力フィールド | 常時表示 |
| Authorizeボタン | ユーザーコード検証開始ボタン | 常時表示 |

### 認可確認画面

| 項目名 | 説明 | 条件 |
|--------|------|------|
| ページタイトル | 「Authorize device to access to your GitLab account.」 | 常時表示 |
| 確認メッセージ | 認可確認を促すメッセージ | 常時表示 |
| ユーザー情報 | 現在ログイン中のユーザーアバター・名前・ユーザー名 | 常時表示 |
| 管理者警告 | 管理者権限でのアクセスに関する警告 | 管理者ユーザー時 |
| スコープ表示 | 要求されている権限範囲 | スコープ指定時 |
| Confirmボタン | 認可承認ボタン | 常時表示 |

## イベント仕様

### 1-Authorizeボタン押下（ユーザーコード入力）

**トリガー**: 「Authorize」ボタンクリック

**処理フロー**:
1. ユーザーコードの形式検証
2. データベースでのユーザーコード検索
3. 有効期限の確認
4. 認可確認画面へ遷移

**成功時**: 認可確認画面を表示
**失敗時**: エラーメッセージを表示してユーザーコード入力画面に戻る

### 2-Confirmボタン押下（認可確認）

**トリガー**: 「Confirm」ボタンクリック

**処理フロー**:
1. ユーザーコードの再検証
2. デバイスグラントへのユーザー紐付け
3. 認可完了状態への更新
4. デバイス側でのポーリングによるトークン取得を有効化

**成功時**: 認可完了メッセージを表示（デバイス側でトークン取得可能に）
**失敗時**: エラーメッセージを表示

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| 認可承認 | oauth_device_grants | UPDATE | ユーザー紐付け・認可完了 |
| トークン発行時 | oauth_access_tokens | INSERT | アクセストークン作成 |

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

#### oauth_device_grants

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE | resource_owner_id | 認可したユーザーID | 認可承認時 |

#### oauth_access_tokens

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | token | アクセストークン（ハッシュ化） | デバイスポーリング成功時 |
| INSERT | resource_owner_id | ユーザーID | - |
| INSERT | application_id | OAuthアプリケーションID | - |
| INSERT | scopes | 認可されたスコープ | - |
| INSERT | expires_in | 有効期限（秒） | - |

## メッセージ仕様

| メッセージID | 種別 | メッセージ内容 | 表示条件 |
|------------|------|---------------|----------|
| MSG001 | タイトル | Authorize device to access to your GitLab account. | 常時表示 |
| MSG002 | 説明 | Please make sure that you intended to authorize this device. | 認可確認画面 |
| MSG003 | 警告 | You are an administrator, which means authorizing access will allow it to interact with GitLab as an administrator as well. | 管理者ユーザー時 |
| MSG004 | 情報 | Scopes associated with this request: {scopes} | スコープ指定時 |
| MSG005 | エラー | Invalid user code | ユーザーコード無効時 |
| MSG006 | エラー | User code has expired | ユーザーコード期限切れ時 |

## 例外処理

| 例外状況 | 処理内容 |
|---------|---------|
| 未認証ユーザー | ログイン画面にリダイレクト |
| 無効なユーザーコード | エラーメッセージを表示 |
| ユーザーコード期限切れ | エラーメッセージを表示（デバイスで再取得を促す） |
| 既に使用済みのユーザーコード | エラーメッセージを表示 |
| デバイスグラント無効 | エラーメッセージを表示 |

## 備考

- RFC 8628（OAuth 2.0 Device Authorization Grant）に準拠
- Doorkeeper Device Authorization Grant gemにより実装
- デバイスコードの有効期限はデフォルト300秒（5分）
- ポーリング間隔は5秒を推奨
- ユーザーコードは人間が読みやすい形式（例：WDJB-MJHT）
- minimalレイアウトを使用
- Organization（組織）との関連付けが必須

---

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

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

### 推奨読解順序

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

デバイス認可に関連するモデルを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | device_grant.rb | `app/models/doorkeeper/device_authorization_grant/device_grant.rb` | デバイスグラントモデル |

**読解のコツ**: `DeviceGrantMixin` がDoorkeeper gemから提供される主要機能を含む。`organization_id` が必須であることに注意。

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

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | device_authorizations_controller.rb | `app/controllers/oauth/device_authorizations_controller.rb` | デバイス認可コントローラー |

**主要処理フロー**:
- **7-14行目**: `index` アクション - ユーザーコード入力画面表示
- **16-27行目**: `confirm` アクション - ユーザーコード検証・認可確認画面表示
- **18行目**: `device_grant_model.find_by(user_code: user_code)` でデバイスグラント検索
- **20行目**: `@scopes` にスコープを設定

#### Step 3: ビューテンプレートを理解する

画面の構成要素を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | index.html.haml | `app/views/doorkeeper/device_authorization_grant/index.html.haml` | ユーザーコード入力画面 |
| 3-2 | authorize.html.haml | `app/views/doorkeeper/device_authorization_grant/authorize.html.haml` | 認可確認画面 |

**index.html.haml 主要処理フロー**:
- **4行目**: タイトル表示
- **6-10行目**: ユーザーコード入力フォーム
- **12-15行目**: Authorizeボタン

**authorize.html.haml 主要処理フロー**:
- **4行目**: タイトル表示
- **5-6行目**: 確認メッセージ
- **7-13行目**: ユーザー情報表示
- **14-17行目**: 管理者警告（条件付き）
- **19-22行目**: スコープ表示
- **24-32行目**: Confirmボタン付きフォーム

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

```
Oauth::DeviceAuthorizationsController#index
    │
    └─ render 'doorkeeper/device_authorization_grant/index'

Oauth::DeviceAuthorizationsController#confirm (POST /oauth/device/confirm)
    │
    ├─ device_grant_model.find_by(user_code: user_code)
    │      └─ DeviceGrant 検索
    │
    ├─ @scopes = device_grant&.scopes
    │
    └─ render 'doorkeeper/device_authorization_grant/authorize'

Doorkeeper::DeviceAuthorizationGrant::DeviceAuthorizationsController#authorize
    │
    ├─ device_grant の検証
    │
    ├─ resource_owner_id の設定
    │
    └─ デバイス側でのトークン取得を有効化
```

### データフロー図

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

デバイスでの認可開始   ───▶ POST /oauth/device_authorization ───▶ device_code, user_code
                             │                                     verification_uri
                             └─ DeviceGrant.create ───▶ oauth_device_grants INSERT

ユーザーコード入力     ───▶ POST /oauth/device/confirm ───▶ 認可確認画面
(user_code)                  │
                             └─ DeviceGrant.find_by(user_code)

認可承認              ───▶ POST /oauth/device_authorizations/authorize ───▶ 認可完了
                             │
                             └─ DeviceGrant.update(resource_owner_id) ───▶ UPDATE

デバイスでのポーリング ───▶ POST /oauth/token (device_code) ───▶ access_token
                             │
                             └─ AccessToken.create ───▶ oauth_access_tokens INSERT
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| device_authorizations_controller.rb | `app/controllers/oauth/device_authorizations_controller.rb` | コントローラー | デバイス認可処理の制御 |
| index.html.haml | `app/views/doorkeeper/device_authorization_grant/index.html.haml` | ビュー | ユーザーコード入力画面 |
| authorize.html.haml | `app/views/doorkeeper/device_authorization_grant/authorize.html.haml` | ビュー | 認可確認画面 |
| device_grant.rb | `app/models/doorkeeper/device_authorization_grant/device_grant.rb` | モデル | デバイスグラントモデル |
| doorkeeper_device_authorization_grant.rb | `config/initializers/doorkeeper_device_authorization_grant.rb` | 設定 | デバイス認可設定 |
| device_auth.rb | `config/routes/device_auth.rb` | 設定 | デバイス認可ルーティング |
