# 画面設計書 2-PKCE認証画面

## 概要

本ドキュメントは、Decap CMS の PKCE認証画面（PKCEAuthenticationPage）の画面設計書である。OAuth 2.0 PKCEフローを用いて、GitHub・GitLab・Bitbucket等の外部プロバイダーへの認証を行う画面の仕様を定義する。

### 本画面の処理概要

**業務上の目的・背景**：Decap CMSではGitリポジトリをバックエンドとして使用するため、リポジトリホスティングサービスへのアクセス権限が必要となる。git-gateway以外のバックエンド（github, gitlab, bitbucket等）を使用する場合、OAuth 2.0 PKCEフローによる認証を行う。PKCEフローはサーバーサイドシークレットを必要とせず、SPAアプリケーションに適したセキュアな認証方式である。

**画面へのアクセス方法**：CMSのURLにアクセスした際、設定ファイルの読み込みが完了し、ユーザーが未認証でかつバックエンド設定がgit-gateway以外の場合に自動的に表示される。App.jsの`authenticating`メソッド（行127）でバックエンドの`authComponent()`により動的に選択される。

**主要な操作・処理内容**：
1. ログインボタンをクリックすると、PkceAuthenticatorの`authenticate`メソッドが呼ばれ、外部プロバイダーの認証ページにリダイレクトされる
2. 外部プロバイダーでの認証後、コールバックURLにリダイレクトされ、`completeAuth`メソッドがトークンを処理する
3. アクセストークンとIDトークンからJWTをデコードし、ユーザー情報（メール、氏名、アバターURL）を抽出する
4. 認証成功時に`onLogin`コールバックが呼ばれ、CMSへのアクセスが許可される

**画面遷移**：
- 遷移元：ローディング画面（設定読み込み完了後、未認証かつgit-gateway以外の場合）
- 遷移先：外部プロバイダー認証ページ（リダイレクト）
- 遷移先：コレクション一覧画面（認証成功後）
- 遷移先：PKCE認証画面（認証失敗時、エラーメッセージ付きで自画面に留まる）

**権限による表示制御**：認証前の画面であるため、権限による表示制御はない。バックエンド設定（config.backend.name）により画面コンポーネント自体が選択される。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 4 | ユーザー認証 | 主機能 | OAuth 2.0 PKCEフローによるログイン処理。外部プロバイダーへの認証リダイレクト |
| 5 | OAuth認証ライブラリ | 補助機能 | PkceAuthenticatorクラスを使用してPKCEフローの初期化・コールバック処理・トークン取得を実行 |
| 6 | 認証UI | 主機能 | PKCEフロー用のログインボタンUIを表示。AuthenticationPageコンポーネントによるレイアウト描画 |

## 画面種別

認証（ログイン）

## URL/ルーティング

- URL変更を伴わない。App.jsの`authenticating`メソッドにより、認証前状態で表示される。
- 外部プロバイダーからのコールバック時にはURLのhash部分にトークン情報が含まれる場合がある。

## 入出力項目

### 入力項目

- 入力フォームなし（ログインボタンのみ）

### 設定項目（config.ymlから取得）

| 項目名 | 設定パス | デフォルト値 | 説明 |
|--------|---------|-------------|------|
| base_url | config.backend.base_url / config.auth.base_url | '' | OAuthサーバーのベースURL |
| app_id | config.backend.app_id / config.auth.app_id | '' | OAuthアプリケーションID |
| auth_endpoint | config.backend.auth_endpoint / config.auth.auth_endpoint | 'oauth2/authorize' | 認証エンドポイントパス |
| auth_token_endpoint | config.backend.auth_token_endpoint / config.auth.auth_token_endpoint | 'oauth2/token' | トークンエンドポイントパス |
| auth_token_endpoint_content_type | config.auth.auth_token_endpoint_content_type | 'application/x-www-form-urlencoded; charset=utf-8' | トークンリクエストのContent-Type |
| use_oidc | config.auth.use_oidc | false | OpenID Connect使用フラグ |
| scope | config.auth.scope / config.auth_scope | 'openid email' | OAuthスコープ |
| email_claim | config.auth.email_claim | 'email' | JWTからメールを取得するクレーム名 |
| full_name_claim | config.auth.full_name_claim | undefined | JWTからフルネームを取得するクレーム名 |
| first_name_claim | config.auth.first_name_claim | undefined | JWTからファーストネームを取得するクレーム名 |
| last_name_claim | config.auth.last_name_claim | undefined | JWTからラストネームを取得するクレーム名 |
| avatar_url_claim | config.auth.avatar_url_claim | undefined | JWTからアバターURLを取得するクレーム名 |

## 表示項目

| 項目名 | 表示条件 | 説明 |
|--------|---------|------|
| ロゴ画像 | config.logo_urlまたはconfig.logo.srcが設定されている場合 | カスタムロゴ画像。未設定時はDecap CMSデフォルトロゴ |
| ログインボタン | 常時 | リンクアイコン付きのログインボタン。テキストは「Login」またはログイン中は「Logging in...」 |
| エラーメッセージ | loginError stateが存在する場合 | 認証エラーメッセージ |
| サイトへ戻るボタン | config.site_urlが設定されている場合 | サイトURLへの戻りリンク |

## イベント仕様

### 1-ログインボタン押下

1. `handleLogin`メソッドが呼ばれる（PKCEAuthenticationPage.js 行122-132）
2. `event.preventDefault()`でデフォルト動作を阻止
3. scopeを`config.auth.scope`または`config.auth_scope`から取得（デフォルト: 'openid email'）
4. `this.auth.authenticate({ scope }, callback)`でPKCEフロー開始
5. PkceAuthenticatorが認証URLを生成し、外部プロバイダーにリダイレクト
6. エラー発生時: `this.setState({ loginError: err.toString() })`でエラーを表示

### 2-コンポーネントマウント時のコールバック処理

1. `componentDidMount`でPkceAuthenticatorを初期化（行52-120）
2. バックエンド設定と`config.auth`設定からOAuth設定を構築
3. `this.auth.completeAuth(callback)`で認証コールバックを処理
4. コールバックデータにaccess_tokenが含まれる場合: JWTデコードしてユーザー情報を抽出
5. id_tokenが含まれる場合: JWTデコードしてユーザー情報を抽出
6. `normalizeClaimsToUser`関数でクレームからユーザーメタデータに正規化
7. `this.props.onLogin(data)`で認証成功処理を実行

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| 認証コールバック完了 | Redux State (auth) | UPDATE | AUTH_REQUEST -> AUTH_SUCCESS でauth stateを更新 |

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

#### Redux State: auth

| 操作 | 項目（キー） | 更新値・取得条件 | 備考 |
|-----|-------------|-----------------|------|
| UPDATE | isFetching | true -> false | 認証処理完了時 |
| UPDATE | user | access_token, token, email, user_metadata等を含むオブジェクト | 認証成功時に設定 |
| UPDATE | error | エラーオブジェクト | 認証失敗時に設定 |

## メッセージ仕様

| メッセージID | 種別 | メッセージ内容 | 表示条件 |
|-------------|------|--------------|---------|
| auth.loggingIn | 情報 | ログイン処理中表示 | inProgress=true時のボタンラベル |
| auth.login | 情報 | ログインボタンラベル | inProgress=false時のボタンラベル |
| ui.toast.onFailToAuth | エラー | 認証失敗トースト通知 | loginUserアクションで認証が失敗した場合 |

## 例外処理

| 例外条件 | 処理内容 | 画面表示 |
|---------|---------|---------|
| 外部プロバイダー認証失敗 | completeAuthのコールバックでerr取得 | loginErrorとしてエラーメッセージ表示 |
| トークン取得失敗 | authenticateのコールバックでerr取得 | loginErrorとしてエラーメッセージ表示 |
| JWTデコード失敗 | try-catchでエラーを無視 | エラー表示なし（ユーザー情報の一部が欠落する可能性） |
| OAuthサーバー設定不正 | PkceAuthenticator初期化時にエラー | loginErrorとしてエラーメッセージ表示 |

## 備考

- PKCEフローは認証コードの横取り攻撃を防ぐためにコードチャレンジ/コードベリファイアを使用する。
- `config.auth`は新しい設定方式であり、`config.backend`の同名プロパティより優先される（PKCEAuthenticationPage.js 行53-73）。
- `normalizeClaimsToUser`関数はJWTクレームからユーザー情報へのマッピングを行い、設定可能なクレーム名をサポートする。
- JWTデコードには`jwt-decode`ライブラリが使用される。デコード失敗時はサイレントに無視される（行102-107, 110-115）。
- ボタンにはリンクアイコン（`<LoginButtonIcon type="link" />`）が表示される。

---

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

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

### 推奨読解順序

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

PKCEフローの設定構造と、認証後のユーザーデータ構造を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | PKCEAuthenticationPage.js | `packages/decap-cms-ui-auth/src/PKCEAuthenticationPage.js` | componentDidMount（行52-120）の設定パラメータ構造。バックエンド設定と新しいauth設定のフォールバック関係 |
| 1-2 | auth.ts | `packages/decap-cms-core/src/actions/auth.ts` | 認証アクションの型定義 |

**読解のコツ**: 行53-73の設定パラメータのデフォルト値とフォールバックの関係に注意。`config.auth`の値が存在しない場合は`config.backend`の値がフォールバックとして使用される。

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

App.jsからPKCEAuthenticationPageがどのように呼び出されるかを把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | App.js | `packages/decap-cms-core/src/components/App/App.js` | authenticatingメソッド（行112-139）でbackend.authComponent()によりPKCEAuthenticationPageが選択される仕組み |

**主要処理フロー**:
1. **行114**: `currentBackend(this.props.config)`でバックエンドインスタンスを取得
2. **行127**: `React.createElement(backend.authComponent(), { onLogin, error, inProgress, config, ... })` でコンポーネントを動的生成

#### Step 3: PKCE認証フローを理解する

PkceAuthenticatorの初期化と認証コールバック処理を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | PKCEAuthenticationPage.js | `packages/decap-cms-ui-auth/src/PKCEAuthenticationPage.js` | componentDidMount（行52-120）のPkceAuthenticator初期化とcompleteAuth処理 |

**主要処理フロー**:
- **行53-73**: バックエンド設定とauth設定からOAuthパラメータを構築
- **行75-81**: normalizeClaimsToUser関数を設定クレーム名で初期化
- **行83-90**: PkceAuthenticatorインスタンスを作成
- **行93-119**: completeAuthでコールバック処理。access_tokenとid_tokenのJWTデコード
- **行99**: `data.user_metadata = {}`で空のメタデータオブジェクトを初期化
- **行100-108**: access_tokenのJWTデコードとクレーム正規化
- **行109-116**: id_tokenのJWTデコードとクレーム正規化

#### Step 4: クレーム正規化を理解する

normalizeClaimsToUser関数のユーザー情報マッピングロジックを把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | PKCEAuthenticationPage.js | `packages/decap-cms-ui-auth/src/PKCEAuthenticationPage.js` | normalizeClaimsToUser関数（行12-40）のクレームからユーザー情報への変換ロジック |

**主要処理フロー**:
- **行22-24**: emailクレームからuser.emailを設定
- **行25-27**: full_nameクレームからuser_metadata.full_nameを設定
- **行28-35**: first_name/last_nameクレームからfull_nameを合成
- **行36-38**: avatar_urlクレームからuser_metadata.avatar_urlを設定

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

```
App.js (render)
    |
    +-- authenticating() [行112-139]
            |
            +-- currentBackend(config)
            +-- backend.authComponent() -> PKCEAuthenticationPage
                    |
                    +-- componentDidMount [行52-120]
                    |       |
                    |       +-- new PkceAuthenticator({ base_url, app_id, ... })
                    |       +-- this.auth.completeAuth(callback)
                    |       |       |
                    |       |       +-- jwtDecode(access_token)
                    |       |       +-- jwtDecode(id_token)
                    |       |       +-- normalizeClaimsToUser(data, claims)
                    |       |       +-- onLogin(data)
                    |       |
                    |       +-- normalizeClaimsToUser() [行12-40]
                    |
                    +-- handleLogin [行122-132]
                            |
                            +-- this.auth.authenticate({ scope }, callback)
                            +-- onLogin(data) -> App.handleLogin()
                                    |
                                    +-- loginUser(credentials) [auth.ts]
```

### データフロー図

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

config.yml設定      ──▶  PkceAuthenticator初期化             ──▶  認証URL生成
(base_url, app_id等)                                              (外部リダイレクト)

コールバックURL     ──▶  completeAuth()                      ──▶  Redux State (auth.user)
(code, state等)          |                                        - token
                         +-- jwtDecode(access_token)              - email
                         +-- jwtDecode(id_token)                  - user_metadata
                         +-- normalizeClaimsToUser()                (full_name, avatar_url)
                         |
                         +-- onLogin(data)
                         +-- loginUser(credentials)
                         +-- backend.authenticate()
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| PKCEAuthenticationPage.js | `packages/decap-cms-ui-auth/src/PKCEAuthenticationPage.js` | ソース | PKCE認証画面のメインコンポーネント |
| AuthenticationPage.js | `packages/decap-cms-ui-default/src/AuthenticationPage.js` | ソース | 認証ページの共通UIレイアウトコンポーネント |
| App.js | `packages/decap-cms-core/src/components/App/App.js` | ソース | アプリケーションルート。認証コンポーネントの動的選択 |
| auth.ts | `packages/decap-cms-core/src/actions/auth.ts` | ソース | 認証関連のReduxアクション |
| PkceAuthenticator | `packages/decap-cms-lib-auth/` | ソース | PKCEフローの実装ライブラリ |
| index.js | `packages/decap-cms-ui-auth/src/index.js` | ソース | 認証UIパッケージのエクスポート定義 |
