# 画面設計書 1-ログイン画面

## 概要

本ドキュメントは、Fizzyアプリケーションのログイン画面（`app/views/sessions/new.html.erb`）の設計仕様を記述したものです。

### 本画面の処理概要

ログイン画面は、ユーザーがFizzyにサインインするためのエントリーポイントとなる画面です。パスワードを使用しないマジックリンク認証を採用しており、メールアドレスを入力するとワンタイムコードが記載されたメールが送信されます。

**業務上の目的・背景**：パスワード管理の煩雑さやセキュリティリスクを軽減するため、マジックリンク認証を採用しています。ユーザーはパスワードを覚える必要がなく、メールアドレスのみで安全にサインインできます。これにより、パスワード漏洩リスクの排除と、ユーザー体験の向上を両立しています。

**画面へのアクセス方法**：未認証状態でアプリケーションにアクセスした場合、または認証が必要なページにアクセスした際に自動的にリダイレクトされます。URL直接アクセス（`/session/new`）でも表示可能です。

**主要な操作・処理内容**：
1. メールアドレスを入力フィールドに入力する
2. 「Let's go」ボタンをクリックしてフォームを送信する
3. 入力されたメールアドレスに対応するIdentityが存在する場合、マジックリンクメールが送信される
4. Identityが存在せず、サインアップが許可されている場合は新規アカウント作成フローに進む
5. サインアップが許可されていない場合でも、セキュリティ上の理由から同様の応答が返される

**画面遷移**：
- 遷移元：認証が必要な任意の画面（未認証時）、サインアップ画面からのリンク
- 遷移先：マジックリンク送信完了画面（`/session/magic_link`）、サインアップ画面（`/signup/new`）

**権限による表示制御**：
- 未認証ユーザーのみアクセス可能（認証済みの場合はルートにリダイレクト）
- マルチテナントモードが有効または初回セットアップ時のみサインアップリンクが表示される

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 2 | サインイン（マジックリンク） | 主機能 | メールアドレス入力とマジックリンク送信処理 |
| 1 | サインアップ | 補助機能 | 新規ユーザーの場合はサインアップ処理へ遷移 |

## 画面種別

登録（メールアドレス入力）

## URL/ルーティング

| 項目 | 値 |
|------|-----|
| URL | `/session/new` |
| HTTPメソッド | GET（表示）、POST（送信先: `/session`） |
| コントローラー | `SessionsController` |
| アクション | `new`（表示）、`create`（送信処理） |
| レイアウト | `public` |

## 入出力項目

### 入力項目

| No | 項目名 | 物理名 | データ型 | 必須 | 最大長 | 入力形式 | バリデーション | 備考 |
|----|--------|--------|---------|------|--------|----------|---------------|------|
| 1 | メールアドレス | email_address | string | 必須 | - | email | RFC準拠のメール形式 | autocomplete="username" |

### 出力項目

なし（フォーム画面のため）

## 表示項目

| No | 項目名 | 表示条件 | データソース | 備考 |
|----|--------|----------|-------------|------|
| 1 | タイトル「Get into Fizzy」 | 常時 | 固定文言 | - |
| 2 | メールアドレス入力フィールド | 常時 | - | placeholder="Enter your email address..." |
| 3 | サインアップ案内文 | Account.accepting_signups? == true | 固定文言 | 新規登録リンク含む |
| 4 | サインイン案内文 | Account.accepting_signups? == false | 固定文言 | シングルテナントモード時 |
| 5 | 「Let's go」ボタン | 常時 | 固定文言 | 矢印アイコン付き |
| 6 | フッター | 常時 | _footer部分テンプレート | サポートメール連絡先 |

## イベント仕様

### 1-「Let's go」ボタン押下

**トリガー**: フォーム送信（submit）

**処理フロー**:
1. `SessionsController#create`が呼び出される
2. レート制限チェック（3分間に10回まで）
3. 入力されたメールアドレスで`Identity`を検索
4. 分岐処理：
   - **Identityが存在する場合**: `identity.send_magic_link`を呼び出し、マジックリンクメールを送信
   - **Identityが存在せず、サインアップ許可時**: `Signup`モデルで新規Identity作成後、マジックリンクメール送信
   - **Identityが存在せず、サインアップ不許可時**: セキュリティのためフェイクのマジックリンク画面にリダイレクト
5. マジックリンク送信完了画面（`/session/magic_link`）にリダイレクト

**レスポンス**:
- HTML: マジックリンク送信完了画面にリダイレクト
- JSON: `{ pending_authentication_token: "..." }`（status: 201）

### 2-サインアップリンク押下

**トリガー**: 「Sign up」リンクのクリック

**処理**: サインアップ画面（`/signup/new`）に遷移

**表示条件**: `Account.accepting_signups?`がtrueの場合のみリンク表示

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| メールアドレス送信（既存ユーザー） | magic_links | INSERT | マジックリンクレコード作成 |
| メールアドレス送信（新規ユーザー） | identities | INSERT | 新規Identityレコード作成 |
| メールアドレス送信（新規ユーザー） | magic_links | INSERT | マジックリンクレコード作成 |

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

#### identities（新規ユーザーの場合のみ）

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | email_address | 入力されたメールアドレス（正規化済み） | 小文字・トリム処理 |
| INSERT | created_at | 現在日時 | 自動設定 |
| INSERT | updated_at | 現在日時 | 自動設定 |

#### magic_links

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | identity_id | 対象IdentityのID | 外部キー |
| INSERT | code | ランダム生成6文字コード | ユニーク制約あり |
| INSERT | purpose | sign_in または sign_up | enum値 |
| INSERT | expires_at | 現在時刻 + 15分 | 有効期限 |
| INSERT | created_at | 現在日時 | 自動設定 |
| INSERT | updated_at | 現在日時 | 自動設定 |

## メッセージ仕様

| No | メッセージ種別 | メッセージ内容 | 表示条件 |
|----|--------------|---------------|---------|
| 1 | エラー | Try again later. | レート制限超過時（3分間に10回以上のリクエスト） |
| 2 | エラー | Something went wrong | サインアップ処理でバリデーションエラー発生時 |

## 例外処理

| 例外パターン | 対応内容 | 備考 |
|------------|---------|------|
| レート制限超過 | alertメッセージ「Try again later.」を表示してログイン画面にリダイレクト | 3分間に10回まで |
| 存在しないメールアドレス（サインアップ不許可時） | フェイクのマジックリンク画面にリダイレクト | セキュリティ対策（メールアドレス存在確認攻撃防止） |
| メールアドレス形式不正 | HTMLのrequired属性とemail型によるブラウザバリデーション | サーバーサイドでもURI::MailTo::EMAIL_REGEXPで検証 |

## 備考

- 本画面はパスワードレス認証（マジックリンク認証）を採用しているため、パスワード入力フィールドは存在しない
- セキュリティ対策として、存在しないメールアドレスでも同様の応答を返すことで、メールアドレスの存在有無を判別されることを防いでいる
- `Account.accepting_signups?`はマルチテナントモード設定またはアカウントが存在しない場合にtrueを返す
- 開発環境では、マジックリンクコードがフラッシュメッセージとレスポンスヘッダー（X-Magic-Link-Code）に含まれる

---

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

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

### 推奨読解順序

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

まず、認証に関わるデータモデルの構造を理解することが重要です。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | identity.rb | `app/models/identity.rb` | Identityモデルの関連（sessions, magic_links, users）と`send_magic_link`メソッドの理解 |
| 1-2 | magic_link.rb | `app/models/magic_link.rb` | マジックリンクのコード生成、有効期限、consumeロジック |
| 1-3 | signup.rb | `app/models/signup.rb` | 新規サインアップ時のIdentity作成フロー |

**読解のコツ**: RailsのActiveRecord関連（has_many, belongs_to）と、concerns（Joinable, Transferable）の役割を把握する。`send_magic_link`がMagicLinkMailerを呼び出す流れを追う。

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

処理の起点となるコントローラーを特定します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | sessions_controller.rb | `app/controllers/sessions_controller.rb` | new/createアクションの処理フロー、レート制限の実装 |

**主要処理フロー**:
1. **8-9行目**: `new`アクション - 画面表示のみ
2. **11-19行目**: `create`アクション - 認証処理の分岐（既存ユーザー/新規/不許可）
3. **53-55行目**: `sign_in`プライベートメソッド - マジックリンク送信
4. **57-69行目**: `sign_up`プライベートメソッド - 新規ユーザーのIdentity作成とマジックリンク送信

#### Step 3: 認証モジュールを理解する

セッション管理とマジックリンク処理のモジュールを確認します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | authentication.rb | `app/controllers/concerns/authentication.rb` | 認証の基本フロー、セッション管理 |
| 3-2 | via_magic_link.rb | `app/controllers/concerns/authentication/via_magic_link.rb` | マジックリンク関連のリダイレクト処理、Cookie設定 |

**主要処理フロー**:
- **25-34行目（via_magic_link.rb）**: `redirect_to_session_magic_link` - マジックリンク送信後のリダイレクト処理
- **43-50行目（via_magic_link.rb）**: `set_pending_authentication_token` - 認証待ちCookieの設定

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

画面表示のテンプレートを確認します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | new.html.erb | `app/views/sessions/new.html.erb` | フォーム構造、条件分岐による表示制御 |
| 4-2 | _footer.html.erb | `app/views/sessions/_footer.html.erb` | フッター部分テンプレート |

**主要処理フロー**:
- **6-23行目**: フォーム定義とemail_field
- **13-17行目**: `Account.accepting_signups?`による条件分岐

#### Step 5: メール送信を理解する

マジックリンクメールの送信処理を確認します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | magic_link_mailer.rb | `app/mailers/magic_link_mailer.rb` | メール送信処理 |

**主要処理フロー**:
- **2-7行目**: `sign_in_instructions`メソッド - マジックリンクコードを含むメール送信

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

```
SessionsController#create
    │
    ├─ Identity.find_by(email_address:)
    │      └─ [既存ユーザーの場合]
    │             └─ sign_in(identity)
    │                    └─ identity.send_magic_link
    │                           ├─ MagicLink.create!
    │                           └─ MagicLinkMailer.sign_in_instructions.deliver_later
    │
    ├─ [新規ユーザー && サインアップ許可の場合]
    │      └─ sign_up
    │             ├─ Signup.new
    │             ├─ signup.valid?(:identity_creation)
    │             └─ signup.create_identity
    │                    ├─ Identity.find_or_create_by!
    │                    └─ identity.send_magic_link(for: :sign_up)
    │
    └─ [新規ユーザー && サインアップ不許可の場合]
           └─ redirect_to_fake_session_magic_link
                  └─ redirect_to_session_magic_link(fake_magic_link)
```

### データフロー図

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

email_address ───▶ SessionsController#create
                          │
                          ├──▶ Identity検索
                          │        │
                          │        ├─[存在]─▶ MagicLink作成 ─▶ メール送信
                          │        │                              │
                          │        └─[不在]─▶ Signup処理         │
                          │                     │                 │
                          │                     └─▶ Identity作成 ─┘
                          │
                          └──▶ Cookie設定 ─▶ リダイレクト ─▶ /session/magic_link
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| sessions_controller.rb | `app/controllers/sessions_controller.rb` | コントローラー | 認証処理の制御 |
| new.html.erb | `app/views/sessions/new.html.erb` | ビュー | ログイン画面テンプレート |
| _footer.html.erb | `app/views/sessions/_footer.html.erb` | パーシャル | フッター表示 |
| identity.rb | `app/models/identity.rb` | モデル | ユーザー識別情報の管理 |
| magic_link.rb | `app/models/magic_link.rb` | モデル | マジックリンクの管理 |
| signup.rb | `app/models/signup.rb` | モデル | サインアップ処理 |
| authentication.rb | `app/controllers/concerns/authentication.rb` | Concern | 認証共通処理 |
| via_magic_link.rb | `app/controllers/concerns/authentication/via_magic_link.rb` | Concern | マジックリンク認証処理 |
| magic_link_mailer.rb | `app/mailers/magic_link_mailer.rb` | メーラー | マジックリンクメール送信 |
| multi_tenantable.rb | `app/models/account/multi_tenantable.rb` | Concern | マルチテナント判定ロジック |
| routes.rb | `config/routes.rb` | 設定 | ルーティング定義 |
