# 通知設計書 59-two_factor_otp_attempt_failed_email

## 概要

本ドキュメントは、GitLabにおける2要素認証失敗通知（two_factor_otp_attempt_failed_email）の設計内容を記載する。

### 本通知の処理概要

**業務上の目的・背景**：2要素認証（2FA）はアカウントセキュリティの重要な層である。誤った2FAコードが入力された場合、それが単純な入力ミスなのか、悪意のある第三者によるアカウント侵害の試みなのかをユーザーが判断できるようにすることが重要である。本通知は、2FAコード入力失敗をユーザーに通知し、不正アクセスの早期発見を可能にする。

**通知の送信タイミング**：ユーザーがワンタイムパスワード（OTP）による2要素認証に失敗した時点で即座に通知が送信される。

**通知の受信者**：2FA認証に失敗したユーザー本人。ユーザーが通知を受信できる権限（receive_notifications）を持っている必要がある。

**通知内容の概要**：認証試行が行われたIPアドレス、日時、GitLabホスト名が含まれる。

**期待されるアクション**：受信者は通知を受け取った後、自身による認証失敗であれば無視し、心当たりがない場合はパスワードの変更やリカバリーコードの確認を行う。

## 通知種別

メール

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 非同期（deliver_later） |
| 優先度 | 高（セキュリティ通知） |
| リトライ | Sidekiq標準リトライ機構に依存 |

### 送信先決定ロジック

認証試行したユーザーの`notification_email_or_default`メソッドで取得されるメールアドレスに送信される。ユーザーが`can?(:receive_notifications)`を満たす場合のみ送信が実行される。

## 通知テンプレート

### メール通知の場合

| 項目 | 内容 |
|-----|------|
| 送信元アドレス | GitLab設定に基づく |
| 送信元名称 | GitLab |
| 件名 | "Attempted sign in to {host} using an incorrect verification code" |
| 形式 | HTML/テキスト |

### 本文テンプレート

```
{host}へのサインイン試行で、誤った確認コードが使用されました。

IPアドレス: {ip}
日時: {time}

このサインイン試行に心当たりがない場合は、アカウントのセキュリティを確認してください。
```

### 添付ファイル

なし

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| @user | 通知対象ユーザー | User | Yes |
| @ip | 認証試行元IPアドレス | request.remote_ip | Yes |
| @time | 認証試行日時 | Time.current | Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| 画面操作 | 2FA OTP認証失敗 | ユーザーが通知可能 | OTPコード入力が失敗した時 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| ユーザーが通知不可 | user.can?(:receive_notifications)がfalseの場合 |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[2FAコード入力] --> B[valid_otp_attempt?]
    B -->|成功| C[サインイン完了]
    B -->|失敗| D[send_two_factor_otp_attempt_failed_email]
    D --> E[NotificationService.two_factor_otp_attempt_failed]
    E --> F{通知可能?}
    F -->|No| G[処理スキップ]
    F -->|Yes| H[Emails::Profile.two_factor_otp_attempt_failed_email]
    H --> I[deliver_later]
    I --> J[handle_two_factor_failure]
    G --> J
    J --> K[failed_attempts増加]
    K --> L[ログイン画面再表示]
```

## データベース参照・更新仕様

### 参照テーブル一覧

| テーブル名 | 用途 | 備考 |
|-----------|------|------|
| users | ユーザー情報取得 | |

### テーブル別参照項目詳細

#### users

| 参照項目（カラム名） | 用途 | 取得条件 |
|-------------------|------|---------|
| notification_email | 通知先メールアドレス | |

### 更新テーブル一覧

| テーブル名 | 操作 | 概要 |
|-----------|------|------|
| users | UPDATE | failed_attemptsを増加（別処理） |

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| 送信失敗 | メール送信エラー | Sidekiqリトライ |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | Sidekiq標準（25回） |
| リトライ間隔 | 指数バックオフ |
| リトライ対象エラー | 一時的なエラー全般 |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | GitLab設定に依存 |
| 1日あたり上限 | 特になし |

### 配信時間帯

制限なし（イベント発生時に即座に送信）

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

- セキュリティ通知のため、重要度が高い
- IPアドレスは個人情報に該当するため、適切に取り扱う
- 認証失敗の通知により、ブルートフォース攻撃の検知が可能
- 通知送信と並行してfailed_attemptsが増加し、アカウントロックに繋がる可能性

## 備考

- OTP認証失敗時のみ送信（WebAuthn失敗時は送信されない）
- authenticate_with_two_factor_via_otpメソッド内で呼び出し

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | user.rb | `app/models/user.rb` | otp関連メソッド、failed_attemptsを確認 |

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | authenticates_with_two_factor.rb | `app/controllers/concerns/authenticates_with_two_factor.rb` | authenticate_with_two_factor_via_otpメソッド |

**主要処理フロー**:
1. **106行目**: authenticate_with_two_factor_via_otpメソッド
2. **107行目**: valid_otp_attempt?でOTP検証
3. **115行目**: 失敗時にsend_two_factor_otp_attempt_failed_email呼び出し
4. **256-258行目**: send_two_factor_otp_attempt_failed_emailの実装

#### Step 3: NotificationServiceを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | notification_service.rb | `app/services/notification_service.rb` | two_factor_otp_attempt_failedメソッド（189-193行目） |

#### Step 4: メーラーを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | profile.rb | `app/mailers/emails/profile.rb` | two_factor_otp_attempt_failed_emailメソッド（218-231行目） |

**主要処理フロー**:
- **219-221行目**: @user, @ip, @timeを設定
- **225-228行目**: 件名にホスト名を含める

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

```
SessionsController#create
    │
    └─ AuthenticatesWithTwoFactor#authenticate_with_two_factor
           │
           └─ authenticate_with_two_factor_via_otp(user)
                  │
                  ├─ valid_otp_attempt?(user) ... 失敗
                  │
                  ├─ send_two_factor_otp_attempt_failed_email(user)
                  │      └─ NotificationService#two_factor_otp_attempt_failed
                  │             └─ Notify#two_factor_otp_attempt_failed_email
                  │                    └─ email_with_layout
                  │
                  └─ handle_two_factor_failure(user, 'OTP', message)
                         └─ user.increment_failed_attempts!
```

### データフロー図

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

2FAコード入力          ──▶ SessionsController        ──▶ 認証結果
  (OTP code, IP)           ├─ 認証処理                    │
                           └─ AuthenticatesWithTwoFactor  │
                                │                          ▼
                                ├─ OTP検証失敗         ──▶ エラー表示
                                │
                                └─ send_two_factor_otp_  ──▶ メール送信
                                   attempt_failed_email      (deliver_later)
                                       └─ NotificationService
                                              └─ Emails::Profile
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| authenticates_with_two_factor.rb | `app/controllers/concerns/authenticates_with_two_factor.rb` | ソース | 2FA認証処理 |
| notification_service.rb | `app/services/notification_service.rb` | ソース | 通知サービス |
| profile.rb | `app/mailers/emails/profile.rb` | ソース | メーラー |
| notify_preview.rb | `app/mailers/previews/notify_preview.rb` | ソース | メールプレビュー |
