# 機能設計書 20-HTTP認証

## 概要

本ドキュメントは、Ruby on RailsフレームワークにおけるHTTP認証機能の設計を記載する。RailsはHTTP Basic認証、Digest認証、Token認証の3つの標準的な認証方式をサポートする。

### 本機能の処理概要

HTTP認証は、HTTPプロトコルの標準的な認証メカニズムを提供する。Basic認証はユーザー名とパスワードをBase64エンコードして送信し、Digest認証はMD5ハッシュを使用してより安全に認証を行い、Token認証はAPIクライアント向けにBearerトークンを使用する。

**業務上の目的・背景**：
Webアプリケーションでは、特定のリソースへのアクセスを認証済みユーザーに制限する必要がある。HTTP認証は、ブラウザ組み込みの認証ダイアログやAPIクライアントで広くサポートされている。特にAPI、RSSフィード、管理画面等で使用される。セッションベースの認証と異なり、ステートレスなため、APIやマシン間通信に適している。

**機能の利用シーン**：
- 管理画面へのアクセス制限
- API認証
- RSSフィードの認証
- Webhook受信時の認証
- マシン間通信の認証

**主要な処理内容**：
1. Basic認証：ユーザー名/パスワードのBase64デコードと検証
2. Digest認証：nonce生成、MD5ハッシュ検証
3. Token認証：Bearerトークンの抽出と検証
4. 認証失敗時のWWW-Authenticateヘッダー返却
5. 401 Unauthorized応答

**関連システム・外部連携**：
- Action Controller（before_action統合）
- ブラウザ（認証ダイアログ）
- APIクライアント
- OpenSSL（MD5ハッシュ）

**権限による制御**：
- HTTP認証自体がアクセス制御機能
- アクションごとに認証要否を設定可能

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | - |

## 機能種別

セキュリティ / 認証 / アクセス制御

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| Authorization | String | Yes | 認証ヘッダー | Basic/Digest/Bearer形式 |

### 入力データソース

- HTTPリクエストヘッダー（Authorization）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| WWW-Authenticate | String | 認証要求ヘッダー |
| status | Integer | 401 Unauthorized |
| body | String | 認証失敗メッセージ |

### 出力先

- HTTPレスポンスヘッダー/ボディ

## 処理フロー

### 処理シーケンス

```
1. Basic認証
   └─ authenticate_or_request_with_http_basic
      └─ has_basic_credentials?
         └─ decode_credentials (Base64)
         └─ user_name_and_password
         └─ ブロックで検証
      └─ 失敗時 → request_http_basic_authentication

2. Digest認証
   └─ authenticate_or_request_with_http_digest
      └─ validate_digest_response
         └─ decode_credentials_header
         └─ validate_nonce
         └─ expected_response (MD5)
      └─ 失敗時 → authentication_request

3. Token認証
   └─ authenticate_or_request_with_http_token
      └─ token_and_options
         └─ raw_params
         └─ トークン抽出
         └─ ブロックで検証
      └─ 失敗時 → request_http_token_authentication
```

### フローチャート

```mermaid
flowchart TD
    A[HTTPリクエスト] --> B{Authorizationヘッダー?}
    B -->|No| C[WWW-Authenticate返却]
    B -->|Yes| D{認証方式?}
    D -->|Basic| E[Base64デコード]
    E --> F[user:password分割]
    F --> G[検証ブロック呼び出し]
    D -->|Digest| H[credentials解析]
    H --> I[nonce検証]
    I --> J[MD5ハッシュ検証]
    J --> G
    D -->|Token/Bearer| K[トークン抽出]
    K --> L[オプション解析]
    L --> G
    G --> M{検証成功?}
    M -->|Yes| N[アクセス許可]
    M -->|No| C
    C --> O[401 Unauthorized]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-001 | Basic形式 | "Basic " + Base64(user:password) | Basic認証 |
| BR-002 | Digest形式 | nonce, opaque, response等を含む | Digest認証 |
| BR-003 | Token形式 | "Token token=xxx" または "Bearer xxx" | Token認証 |
| BR-004 | secure_compare | タイミング攻撃対策の比較 | 全方式 |
| BR-005 | nonce有効期限 | デフォルト5分 | Digest認証 |

### 計算ロジック

**Digest認証のレスポンス計算**：
```
ha1 = MD5(username:realm:password)
ha2 = MD5(method:uri)
response = MD5(ha1:nonce:nc:cnonce:qop:ha2)
```

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

HTTP認証はデータベースを直接操作しない。

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| - | - | - | なし |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 401 | HTTPステータス | 認証失敗 | 正しい認証情報を送信 |
| ArgumentError | 例外 | name/passwordがString以外 | 文字列を渡す |

### リトライ仕様

- ブラウザは認証ダイアログを再表示
- APIクライアントは再認証が必要

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

特になし

## パフォーマンス要件

- 認証処理はリクエストごとに実行
- MD5計算（Digest）のオーバーヘッドは最小限
- `secure_compare`はタイミング攻撃対策のため固定時間で比較

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

- Basic認証はHTTPSと併用必須（平文送信）
- Digest認証はパスワードのハッシュ保存を推奨（ha1形式）
- Token認証はトークンの安全な管理が必要
- `secure_compare`でタイミング攻撃を防止
- nonce有効期限でリプレイ攻撃を軽減

## 備考

- Basic認証は最もシンプルだがセキュリティは低い
- Digest認証はMD5使用のため、現代では推奨されない
- Token認証はAPI向けに最も適している
- 本格的な認証にはDevise等のgemを推奨

---

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

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

### 推奨読解順序

#### Step 1: Basic認証を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | http_authentication.rb | `actionpack/lib/action_controller/metal/http_authentication.rb` | Basicモジュール |

**読解のコツ**: Basic認証は最もシンプルな実装なので、まずこれを理解する。

**主要処理フロー**（http_authentication.rb）:
- **69-145行目**: `Basic`モジュール
- **72-106行目**: `ControllerMethods`
- **79-83行目**: `http_basic_authenticate_with`クラスメソッド
- **86-93行目**: `http_basic_authenticate_or_request_with`
- **99-101行目**: `authenticate_with_http_basic`
- **108-112行目**: `authenticate`（認証実行）
- **114-116行目**: `has_basic_credentials?`
- **118-119行目**: `user_name_and_password`
- **122-124行目**: `decode_credentials`（Base64デコード）
- **134-136行目**: `encode_credentials`
- **138-144行目**: `authentication_request`（401応答）

#### Step 2: Digest認証を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | http_authentication.rb | `actionpack/lib/action_controller/metal/http_authentication.rb` | Digestモジュール |

**主要処理フロー**:
- **190-353行目**: `Digest`モジュール
- **193-213行目**: `ControllerMethods`
- **216-218行目**: `authenticate`
- **223-243行目**: `validate_digest_response`
- **249-253行目**: `expected_response`（MD5計算）
- **255-257行目**: `ha1`計算
- **268-273行目**: `decode_credentials`
- **332-337行目**: `nonce`生成
- **343-347行目**: `validate_nonce`
- **350-352行目**: `opaque`計算

#### Step 3: Token認証を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | http_authentication.rb | `actionpack/lib/action_controller/metal/http_authentication.rb` | Tokenモジュール |

**主要処理フロー**:
- **427-562行目**: `Token`モジュール
- **428-430行目**: TOKEN_KEY, TOKEN_REGEX, AUTHN_PAIR_DELIMITERS
- **433-457行目**: `ControllerMethods`
- **474-479行目**: `authenticate`
- **496-502行目**: `token_and_options`
- **521-530行目**: `raw_params`
- **541-546行目**: `encode_credentials`
- **557-561行目**: `authentication_request`

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

```
コントローラ
    │
    ├─ http_basic_authenticate_with name:, password:
    │      │
    │      └─ before_action
    │             │
    │             └─ http_basic_authenticate_or_request_with
    │                    │
    │                    └─ authenticate_or_request_with_http_basic
    │                           │
    │                           ├─ Basic.authenticate(request)
    │                           │      │
    │                           │      ├─ has_basic_credentials?
    │                           │      │      └─ authorization.present? && auth_scheme == "basic"
    │                           │      │
    │                           │      └─ login_procedure.call(*user_name_and_password)
    │                           │             │
    │                           │             └─ decode_credentials → Base64.decode64
    │                           │
    │                           └─ request_http_basic_authentication
    │                                  │
    │                                  └─ authentication_request
    │                                         │
    │                                         └─ WWW-Authenticate: Basic realm="..."
    │
    ├─ authenticate_or_request_with_http_digest(realm)
    │      │
    │      └─ Digest.authenticate(request, realm)
    │             │
    │             ├─ validate_digest_response
    │             │      │
    │             │      ├─ decode_credentials_header
    │             │      ├─ validate_nonce
    │             │      └─ expected_response (MD5)
    │             │
    │             └─ password_procedure.call(username)
    │
    └─ authenticate_or_request_with_http_token
           │
           └─ Token.authenticate(controller)
                  │
                  ├─ token_and_options(request)
                  │      │
                  │      └─ raw_params → token抽出
                  │
                  └─ login_procedure.call(token, options)
```

### データフロー図

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

Authorization: Basic xxx
     │
     └─▶ Base64.decode64 ─────▶ "user:password"
                                      │
                                      ▼
                              user_name_and_password
                                      │
                                      ▼
                              login_procedure.call
                                      │
                          ┌───────────┴───────────┐
                          │                       │
                          ▼                       ▼
                       成功                    失敗
                          │                       │
                          ▼                       ▼
                    アクセス許可          WWW-Authenticate: Basic
                                                  │
                                                  ▼
                                          401 Unauthorized


Authorization: Token token="xxx"
     │
     └─▶ raw_params ─────────▶ token抽出
                                      │
                                      ▼
                              login_procedure.call
                                      │
                          ┌───────────┴───────────┐
                          │                       │
                          ▼                       ▼
                       成功                    失敗
                          │                       │
                          ▼                       ▼
                    アクセス許可          WWW-Authenticate: Token
                                                  │
                                                  ▼
                                          401 Unauthorized
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| http_authentication.rb | `actionpack/lib/action_controller/metal/http_authentication.rb` | ソース | Basic/Digest/Token認証 |
| security_utils.rb | `activesupport/lib/active_support/security_utils.rb` | ソース | secure_compare |
