# 機能設計書 6-顧客なりすましログイン

## 概要

本ドキュメントは、Shopware 6 Storefrontにおける顧客なりすましログイン機能の設計仕様を記述する。管理者が顧客としてログインし、顧客の視点でショップを確認する機能である。

### 本機能の処理概要

**業務上の目的・背景**：ECサイトの運営において、カスタマーサポートや問題調査のため、管理者が顧客のアカウントにアクセスして状況を確認する必要がある場合がある。本機能により、管理者は顧客としてStorefrontにログインし、顧客が見ている画面や設定を直接確認できる。

**機能の利用シーン**：カスタマーサポート担当者が顧客からの問い合わせに対応する際、顧客の注文状況やカート内容を確認するために使用される。また、特定の顧客グループに対する表示確認などにも利用される。

**主要な処理内容**：
1. 管理画面から発行されたなりすましトークンを受け取る
2. トークンの有効性を検証（JWT形式）
3. 現在ログイン中の場合はログアウト処理を実行
4. 対象顧客としてログイン
5. コンテキストになりすましユーザーIDを設定
6. アカウントホームページへリダイレクト

**関連システム・外部連携**：管理画面（Admin API）からトークンが発行される

**権限による制御**：有効ななりすましトークンを持つ管理者ユーザーのみが使用可能。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 65 | ユーザー・権限管理 | 補助機能 | 管理者ユーザーとロールの管理 |

## 機能種別

認証処理 / 管理者機能

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| token | string | Yes | なりすましトークン（JWT） | NotBlank |
| customerId | string | No (v6.8.0で削除予定) | 対象顧客ID | UUID形式、顧客存在確認 |
| userId | string | No (v6.8.0で削除予定) | 管理者ユーザーID | UUID形式、ユーザー存在確認 |

### 入力データソース

- POSTリクエストボディ

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| token | string | 新しいセッションコンテキストトークン |

### 出力先

- セッション（コンテキストトークン）
- HTTPレスポンス（リダイレクト）

## 処理フロー

### 処理シーケンス

```
1. リクエスト受信
   └─ AuthController::imitateCustomerLogin がPOSTリクエストを受け取る

2. トークン取得
   └─ リクエストからtokenパラメータを取得

3. トークン検証（v6.8.0以降）
   └─ JWTトークンをデコードして顧客ID、ユーザーID、Sales Channel IDを取得
   └─ Sales Channel IDが現在のコンテキストと一致することを確認

4. トークン検証（v6.8.0以前）
   └─ customerId, userIdをリクエストから取得
   └─ トークンの署名を検証

5. 既存ログインチェック
   └─ 対象顧客が既に現在のセッションでログイン中の場合は現在のトークンを返す

6. 既存セッション処理
   └─ 別の顧客でログイン中の場合はログアウト処理を実行
   └─ 新しいコンテキストを生成

7. なりすましユーザー設定
   └─ コンテキストになりすましユーザーID（管理者ID）を設定

8. 顧客としてログイン
   └─ AccountService::loginById で対象顧客としてログイン

9. レスポンス生成
   └─ 成功時：アカウントホームページへリダイレクト
   └─ 失敗時：ログインページへフォワード（エラー表示）
```

### フローチャート

```mermaid
flowchart TD
    A[なりすまし開始] --> B[トークン取得]
    B --> C{v6.8.0以降?}
    C -->|Yes| D[JWTデコード]
    C -->|No| E[リクエストからID取得]
    D --> F{Sales Channel一致?}
    E --> G[トークン署名検証]
    F -->|No| H[InvalidImitationTokenException]
    F -->|Yes| I{既に対象顧客でログイン中?}
    G --> I
    I -->|Yes| J[現在のトークンを返す]
    I -->|No| K{別の顧客でログイン中?}
    K -->|Yes| L[ログアウト処理]
    K -->|No| M[なりすましユーザー設定]
    L --> N[新規コンテキスト生成]
    N --> M
    M --> O[AccountService::loginById]
    O --> P{ログイン成功?}
    P -->|Yes| Q[アカウントホームへリダイレクト]
    P -->|No| R[ログインページへフォワード]
    H --> R
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-001 | トークン必須 | なりすましにはトークンが必須 | 常時 |
| BR-002 | Sales Channel一致 | トークンのSales Channel IDが現在のコンテキストと一致すること | v6.8.0以降 |
| BR-003 | 既存セッション処理 | 別の顧客でログイン中の場合は先にログアウト | ログイン中時 |
| BR-004 | なりすましユーザー追跡 | imitatingUserIdとして管理者IDをコンテキストに設定 | ログイン成功時 |
| BR-005 | 同一顧客スキップ | 既に対象顧客でログイン中の場合は処理をスキップ | 同一顧客時 |

### 計算ロジック

- トークン検証: JWTのデコードと署名検証（ImitateCustomerTokenGenerator使用）

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 顧客存在確認 | customer | SELECT | 対象顧客の存在を確認 |
| ユーザー存在確認 | user | SELECT | 管理者ユーザーの存在を確認 |
| ログイン更新 | customer | UPDATE | 最終ログイン日時を更新 |
| コンテキスト保存 | sales_channel_api_context | INSERT/UPDATE | セッションコンテキストの保存 |

### テーブル別操作詳細

#### customer

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | id | id = customerId | 存在確認 |
| UPDATE | last_login | new DateTimeImmutable() | ログイン成功時 |

#### user

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | id | id = userId | 存在確認（v6.8.0以前） |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| CHECKOUT__CUSTOMER_INVALID_IMITATE_CUSTOMER_TOKEN | InvalidImitateCustomerTokenException | トークン無効またはSales Channel不一致 | ログインページへフォワード |
| CHECKOUT__CUSTOMER_NOT_FOUND | CustomerNotFoundByIdException | 対象顧客が存在しない | ログインページへフォワード |

### リトライ仕様

- リトライは不要（新しいトークンを発行する必要あり）

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

- ログアウト処理と新規ログインは個別トランザクション

## パフォーマンス要件

- なりすましログイン処理全体で500ms以内のレスポンスを目標

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

- トークンはJWT形式で署名検証される
- Sales Channel IDの一致を確認して異なるショップへのなりすましを防止
- imitatingUserIdにより監査ログで管理者の操作を追跡可能
- トークンは短時間で期限切れとなる設計

## 備考

- v6.8.0以降はトークンからcustomerId, userId, salesChannelIdを取得（リクエストパラメータは不要）
- なりすまし中はimitatingUserIdがコンテキストに設定される
- CustomerLoginEventが発行されるため、通常のログインと同様にイベントリスナーが動作する

---

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

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

### 推奨読解順序

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

なりすましトークンとコンテキストの構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | ImitateCustomerToken.php | `src/Core/Checkout/Customer/Struct/ImitateCustomerToken.php` | customerId, iss(userId), salesChannelIdプロパティ |
| 1-2 | SalesChannelContext.php | `src/Core/System/SalesChannel/SalesChannelContext.php` | imitatingUserId, setImitatingUserIdメソッド |

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

StorefrontのなりすましリクエストはAuthControllerで受け付けられる。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | AuthController.php | `src/Storefront/Controller/AuthController.php` | imitateCustomerLogin()メソッド（357-377行目） |

**主要処理フロー**:
1. **364-365行目**: imitateCustomerRoute呼び出し
2. **367行目**: 成功時はアカウントホームへリダイレクト
3. **368-374行目**: 例外キャッチしてログインページへフォワード

#### Step 3: Store-APIルートを理解する

実際のなりすましロジックはImitateCustomerRouteに実装されている。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | ImitateCustomerRoute.php | `src/Core/Checkout/Customer/SalesChannel/ImitateCustomerRoute.php` | imitateCustomerLogin()メソッド（72-112行目） |

**主要処理フロー**:
- **79行目**: トークン文字列を取得
- **81-89行目**: v6.8.0以前の検証ロジック
- **90-94行目**: v6.8.0以降のJWTデコードとSales Channel確認
- **97-99行目**: 既に対象顧客でログイン中の場合は現在のトークンを返す
- **101-104行目**: 別の顧客でログイン中の場合はログアウト
- **107行目**: なりすましユーザーID設定
- **109行目**: AccountService::loginByIdで顧客としてログイン

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

```
AuthController::imitateCustomerLogin (Storefront)
    │
    └─ ImitateCustomerRoute::imitateCustomerLogin (Store-API)
           │
           ├─ ImitateCustomerTokenGenerator::decode (v6.8.0以降)
           │      └─ JWTデコード + 検証
           │
           ├─ [v6.8.0以前]
           │      ├─ validateRequestDataFields
           │      │      └─ DataValidator::validate
           │      └─ ImitateCustomerTokenGenerator::validate
           │
           ├─ [既に対象顧客] return 現在のトークン
           │
           ├─ [別の顧客でログイン中]
           │      ├─ LogoutRoute::logout
           │      └─ SalesChannelContextFactory::create
           │
           ├─ context.setImitatingUserId
           │
           └─ AccountService::loginById
                  ├─ CustomerBeforeLoginEvent (dispatch)
                  ├─ CustomerRepository::update (lastLogin)
                  ├─ CartRestorer::restore
                  └─ CustomerLoginEvent (dispatch)
```

### データフロー図

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

token ───▶ AuthController::imitateCustomerLogin
               │
               ▼
         ImitateCustomerRoute
               │
               ▼
         トークンデコード/検証
               │
         ┌─────┴─────┐
         ▼           ▼
    [v6.8.0以降]  [v6.8.0以前]
    JWTデコード   リクエストから
                 ID取得+検証
               │
               ▼
         Sales Channel確認
               │
         ┌─────┴─────┐
         ▼           ▼
    既に対象顧客?  別の顧客?
    (return)      (logout)
               │
               ▼
         context.setImitatingUserId
               │
               ▼
         AccountService::loginById
               │
               ▼
         ContextTokenResponse ───▶ [アカウントホーム]
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| AuthController.php | `src/Storefront/Controller/AuthController.php` | コントローラ | なりすましエンドポイント |
| ImitateCustomerRoute.php | `src/Core/Checkout/Customer/SalesChannel/ImitateCustomerRoute.php` | ルート | なりすましのStore-APIルート |
| AbstractImitateCustomerRoute.php | `src/Core/Checkout/Customer/SalesChannel/AbstractImitateCustomerRoute.php` | 抽象クラス | なりすましルートの抽象定義 |
| ImitateCustomerTokenGenerator.php | `src/Core/Checkout/Customer/ImitateCustomerTokenGenerator.php` | サービス | トークン生成・検証 |
| ImitateCustomerToken.php | `src/Core/Checkout/Customer/Struct/ImitateCustomerToken.php` | 構造体 | トークンデータ構造 |
| AccountService.php | `src/Core/Checkout/Customer/SalesChannel/AccountService.php` | サービス | ログイン処理 |
| LogoutRoute.php | `src/Core/Checkout/Customer/SalesChannel/LogoutRoute.php` | ルート | ログアウト処理 |
