# 画面設計書 336-OAuthリダイレクト

## 概要

本ドキュメントは、GitLabにおけるOAuth認可リダイレクト画面の設計仕様を定義する。

### 本画面の処理概要

OAuthリダイレクト画面は、OAuth2.0認可フローにおいて、ユーザーがアプリケーションへのアクセスを承認した後、クライアントアプリケーションのコールバックURLにリダイレクトする中間画面である。

**業務上の目的・背景**：OAuth2.0 Authorization Code Grantフローでは、認可承認後にクライアントアプリケーションのコールバックURLに認可コードを含めてリダイレクトする必要がある。本画面は、このリダイレクト処理を安全に実行するための中間ページである。JavaScriptによる自動リダイレクトを使用し、URLフラグメント（ハッシュ）の安全な引き継ぎを行う。また、JavaScriptが無効な環境でも手動でリダイレクトできるリンクを提供する。

**画面へのアクセス方法**：
1. 外部アプリケーションがGitLab OAuth認可エンドポイントにリダイレクト
2. ユーザーがログイン（必要な場合）
3. 認可画面でアプリケーションを承認、または既に承認済みのConfidentialアプリの場合は自動スキップ
4. 本リダイレクト画面が表示され、即座にコールバックURLへリダイレクト

**主要な操作・処理内容**：
1. 自動リダイレクト（JavaScript）
2. 手動リダイレクト（リンククリック、JavaScriptが無効な場合）
3. URLフラグメントの安全な引き継ぎ

**画面遷移**：
- 遷移元：OAuth認可画面（No.9）（自動認可の場合は直接）
- 遷移先：クライアントアプリケーションのコールバックURL

**権限による表示制御**：
- ログインユーザーのみアクセス可能
- 認可フロー経由でのみ到達可能

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 88 | OAuth2プロバイダ | 主機能 | OAuth認可リダイレクト画面表示 |

## 画面種別

リダイレクト（中間画面）

## URL/ルーティング

| メソッド | パス | アクション |
|---------|------|-----------|
| - | - | `oauth/authorizations#new` または `#create` 後の内部レンダリング |

※本画面は認可フローの結果として内部的にレンダリングされ、直接URLアクセスは想定されない。

## 入出力項目

本画面はリダイレクト専用画面であり、ユーザー入力項目はない。

| 項目名 | 入力/出力 | データ型 | 必須 | 説明 |
|--------|----------|---------|------|------|
| redirect_uri | 出力 | String | - | リダイレクト先URL（認可コード含む） |

## 表示項目

| 項目名 | データソース | 表示形式 | 備考 |
|--------|-------------|---------|------|
| ページタイトル | 固定 | 見出し | "Redirecting" |
| リダイレクトリンク | redirect_uri | アンカーリンク | 手動リダイレクト用 |

## イベント仕様

### 1-自動リダイレクト（JavaScript）

**トリガー**: ページロード完了後、即時実行（IIFE）

**処理内容**:
1. 現在のURLのフラグメント（hash）を取得
2. フラグメントが許可された文字パターン（`#[\w-]+`）に一致するか検証
3. リダイレクト先URLにフラグメントが含まれていないことを確認
4. 条件を満たす場合、リダイレクト先URLにフラグメントを付加
5. `window.location`を使用してリダイレクト実行

**セキュリティ考慮**:
- フラグメントは`/^#[\w-]+$/g`パターンにマッチするもののみ許可
- 英数字、アンダースコア、ハイフンのみ受け入れ
- XSSやインジェクション攻撃を防止

---

### 2-手動リダイレクト（リンククリック）

**トリガー**: ユーザーがリダイレクトリンクをクリック

**処理内容**:
1. アンカータグのhref属性で指定されたURLに遷移
2. JavaScriptが無効な環境でのフォールバック

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| 画面表示 | - | - | 本画面自体でのDB更新なし |

※認可コードの生成はリダイレクト画面表示前の処理で完了している。

## メッセージ仕様

| 種別 | メッセージID | メッセージ内容 | 表示条件 |
|------|-------------|---------------|---------|
| 見出し | - | Redirecting | 常時表示 |
| リンクテキスト | - | Click here to redirect to {redirect_uri} | 常時表示 |

## 例外処理

| 例外状況 | 処理内容 | 表示/遷移先 |
|---------|---------|------------|
| JavaScriptが無効 | 手動リンククリックによるリダイレクト | コールバックURL |
| 無効なフラグメント | フラグメントを除外してリダイレクト | コールバックURL |

## 備考

- 本画面は`layout: false`で表示され、最小限のHTMLのみレンダリング
- JavaScriptによる自動リダイレクトが基本動作
- URLフラグメントの引き継ぎは、SPAアプリケーションなどでのステート保持に使用される
- セキュリティのため、フラグメントには厳格なパターンマッチングが適用される
- リダイレクト先URLに既にフラグメントが含まれている場合、追加のフラグメントは付加しない

---

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

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

### 推奨読解順序

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

リダイレクトURIの構造を理解。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | doorkeeper.rb | `config/initializers/doorkeeper.rb` | force_ssl_in_redirect_uri、forbid_redirect_uri設定 |

**読解のコツ**: Doorkeeperの設定で`forbid_redirect_uri`がコールバックとして定義されており、危険なスキーム（data、vbscript、javascript）をブロックしている点に注目。

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

リダイレクト画面がレンダリングされる条件を確認。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | authorizations_controller.rb | `app/controllers/oauth/authorizations_controller.rb` | newアクションのリダイレクト分岐 |

**主要処理フロー**:
1. **行25-26**: `skip_authorization?` または `matching_token?` でConfidentialアプリの自動認可判定
2. **行27-30**: 自動認可の場合、`authorization.authorize`を実行しリダイレクト画面をレンダリング
3. **行30**: `render "doorkeeper/authorizations/redirect", locals: { redirect_uri: parsed_redirect_uri }, layout: false`
4. **行68-76**: `allow_redirect_uri_form_action` - CSPのform-actionディレクティブにリダイレクトスキームを追加

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

リダイレクト画面とJavaScript処理。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | redirect.html.haml | `app/views/doorkeeper/authorizations/redirect.html.haml` | リダイレクト画面テンプレート |

**主要処理フロー**:
- **行1**: ページヘッダー「Redirecting」
- **行3-4**: 手動リダイレクト用アンカーリンク
- **行6-20**: JavaScriptによる自動リダイレクト処理
- **行10**: フラグメントの許可パターン `^#[\w-]+$`
- **行15-17**: フラグメント付加の条件判定
- **行19**: `window.location`によるリダイレクト実行

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

```
OAuth認可フロー
    │
    ├─ GET /oauth/authorize (new)
    │      │
    │      └─ AuthorizationsController#new
    │             │
    │             ├─ pre_auth.authorizable?
    │             │
    │             ├─ skip_authorization? OR matching_token?
    │             │      │
    │             │      └─ Yes: authorization.authorize
    │             │                 │
    │             │                 └─ render "redirect", layout: false
    │             │                        └─ locals: { redirect_uri: ... }
    │             │
    │             └─ No: render "new" (認可確認画面)
    │
    └─ POST /oauth/authorize (create)
           │
           └─ Doorkeeper::AuthorizationsController#create
                  │
                  └─ 認可後、リダイレクト処理
```

### データフロー図

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

認可成功 ───▶ AuthorizationsController
                    │
                    ├─ authorization.authorize
                    │      └─ 認可コード生成、redirect_uri構築
                    │
                    └─ render redirect.html.haml ───▶ ブラウザ
                           │
                           └─ JavaScript実行
                                  │
                                  ├─ URLフラグメント検証
                                  │      └─ /^#[\w-]+$/
                                  │
                                  └─ window.location = redirect_uri ───▶ コールバックURL
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| authorizations_controller.rb | `app/controllers/oauth/authorizations_controller.rb` | コントローラ | 認可フロー制御、リダイレクト判定 |
| redirect.html.haml | `app/views/doorkeeper/authorizations/redirect.html.haml` | テンプレート | リダイレクト画面、JavaScript処理 |
| doorkeeper.rb | `config/initializers/doorkeeper.rb` | 設定 | リダイレクトURI制限設定 |
| PageHeadingComponent | `app/components/layouts/page_heading_component.rb` | コンポーネント | ページヘッダー |
