# 機能設計書 3-ログイン機能

## 概要

本ドキュメントは、InvenTreeモバイルアプリにおけるログイン機能の設計を記載する。ユーザー名とパスワードを使用してInvenTreeサーバーにログインし、認証トークンを取得する機能を提供する。

### 本機能の処理概要

ユーザーが入力した認証情報（ユーザー名・パスワード）を使用してInvenTreeサーバーのトークン取得APIを呼び出し、認証トークンを取得・保存する機能である。取得したトークンはユーザープロファイルに保存され、以降のAPI通信に使用される。

**業務上の目的・背景**：InvenTreeサーバーはトークンベース認証を採用しており、すべてのAPI操作に認証トークンが必要である。初回アクセス時やトークン無効化後にユーザー名・パスワードでトークンを取得し、セキュアなAPI通信を確立する必要がある。

**機能の利用シーン**：新規サーバープロファイル作成後の初回ログイン、トークン期限切れや無効化後の再ログイン、別のユーザーアカウントへの切り替え時などに利用される。

**主要な処理内容**：
1. ユーザー名・パスワードの入力フォーム表示
2. 入力値のバリデーション
3. Basic認証ヘッダーを使用したトークン取得APIの呼び出し
4. 取得したトークンのプロファイルへの保存
5. エラー発生時のエラーメッセージ表示
6. ログイン成功時の前画面への遷移

**関連システム・外部連携**：InvenTree REST API（user/token/エンドポイント）

**権限による制御**：サーバー側でユーザーアカウントの有効/無効が制御される。ログイン後の権限はトークン取得後に別途取得。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 7 | サーバー選択画面 | 遷移元画面 | トークン未取得プロファイル選択時にログイン画面へ遷移 |
| 9 | ログイン画面 | 主画面 | ユーザー名とパスワードでサーバーにログインし認証トークン取得 |

## 機能種別

認証処理 / データ連携

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| profile | UserProfile | Yes | 接続対象のサーバープロファイル | null不可、serverが必須 |
| username | String | Yes | ユーザー名 | 空文字不可 |
| password | String | Yes | パスワード | 空文字不可 |

### 入力データソース

- ログイン画面のテキストフォーム入力

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| token | String | 認証トークン |
| response | APIResponse | API応答（成功/失敗情報含む） |

### 出力先

- UserProfileのtokenフィールド（sembastデータベース経由で永続化）

## 処理フロー

### 処理シーケンス

```
1. ログイン画面表示
   └─ プロファイルのサーバーURL表示
   └─ ユーザー名・パスワード入力フォーム表示

2. フォームバリデーション
   └─ ユーザー名の空チェック
   └─ パスワードの空チェック

3. ローディング表示
   └─ showLoadingOverlay()

4. トークン取得APIコール（fetchToken）
   └─ デバイス情報の取得（プラットフォーム、モデル等）
   └─ トークン名の生成（inventree-mobile-app-{platform}-{device}）
   └─ Basic認証ヘッダーの構築
   └─ user/token/エンドポイントへGETリクエスト

5. レスポンス処理
   └─ 成功: トークンをプロファイルに保存、前画面へ遷移
   └─ 失敗: エラーメッセージ表示

6. ローディング非表示
   └─ hideLoadingOverlay()
```

### フローチャート

```mermaid
flowchart TD
    A[ログイン画面表示] --> B[ユーザー名・パスワード入力]
    B --> C[ログインボタン押下]
    C --> D{バリデーション}
    D -->|NG| E[エラー表示]
    E --> B
    D -->|OK| F[ローディング表示]
    F --> G[fetchToken API呼び出し]
    G --> H{レスポンス確認}
    H -->|成功| I[トークン保存]
    I --> J[ローディング非表示]
    J --> K[前画面へ遷移]
    H -->|401/403| L[認証エラー表示]
    L --> M[ローディング非表示]
    M --> B
    H -->|その他エラー| N[ステータスコードエラー表示]
    N --> M
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | トークン名生成 | トークン名はプラットフォーム・デバイス情報を含む | トークン取得時 |
| BR-02 | Basic認証使用 | トークン取得時のみBasic認証を使用 | fetchToken時 |
| BR-03 | パスワード非保存 | パスワードは保存せず、トークンのみ保存 | ログイン成功時 |

### 計算ロジック

**トークン名生成ロジック**:
```
platform_name = "inventree-mobile-app"
+ "-" + {platform: android/ios/macos/linux/windows}
+ "-" + {deviceInfo.name}
+ "-" + {deviceInfo.model}
+ "-" + {deviceInfo.systemVersion}
```

**Basic認証ヘッダー生成**:
```
"Basic " + base64Encode(utf8.encode("{username}:{password}"))
```

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| トークン保存 | profiles | UPDATE | プロファイルのtokenフィールドを更新 |

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

#### profiles（sembast store）

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE | token | API取得したトークン文字列 | fetchToken成功時 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | バリデーションエラー | ユーザー名が空 | usernameEmptyメッセージ表示 |
| - | バリデーションエラー | パスワードが空 | passwordEmptyメッセージ表示 |
| 401 | 認証エラー | ユーザー名/パスワード不正 | invalidUsernamePasswordメッセージ表示 |
| 403 | 認証エラー | アカウント無効または権限なし | invalidUsernamePasswordメッセージ表示 |
| - | トークン欠落 | レスポンスにtokenフィールドなし | tokenMissingFromResponseメッセージ表示 |
| その他 | ステータスエラー | 上記以外のHTTPエラー | ステータスコードに応じたメッセージ表示 |

### リトライ仕様

- 自動リトライなし
- ユーザーによる手動再入力・再送信が必要

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

- APIリクエストとDB更新は別トランザクション
- API成功後にDB更新が失敗しても、次回起動時に再ログインで復旧可能

## パフォーマンス要件

- APIタイムアウト: 10秒（api.dart共通設定）

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

- パスワードは画面上でマスク表示（obscureText）
- パスワード表示/非表示の切り替え機能あり
- パスワードはローカルに保存しない（トークンのみ保存）
- Basic認証はHTTPS通信を前提

## 備考

- パスワード入力フィールドには表示/非表示切り替えアイコンがある
- キーボードはログイン処理開始時に自動で閉じる

---

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

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

### 推奨読解順序

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

まず、ログイン処理に関連するデータ構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | user_profile.dart | `lib/user_profile.dart` | UserProfileクラスのtokenフィールド（39行目） |
| 1-2 | api.dart | `lib/api.dart` | APIResponseクラス（35-110行目） |

**読解のコツ**: tokenフィールドは空文字がデフォルトで、ログイン成功時に値が設定される。

#### Step 2: UI層を理解する

ログイン画面のUI実装を追う。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | login.dart | `lib/settings/login.dart` | InvenTreeLoginWidgetクラス（11-183行目） |

**主要処理フロー**:
- **21-28行目**: フォームキーと入力変数、パスワードマスク状態の定義
- **31-74行目**: `_doLogin`メソッド - ログイン処理の中核
- **126-144行目**: ユーザー名入力フィールド
- **145-174行目**: パスワード入力フィールド（表示切替含む）

#### Step 3: API層を理解する

トークン取得APIの実装を追う。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | api.dart | `lib/api.dart` | fetchTokenメソッド（547-635行目） |

**主要処理フロー**:
- **556-584行目**: トークン名の生成（プラットフォーム・デバイス情報含む）
- **586-587行目**: Basic認証ヘッダーの構築
- **590-594行目**: user/token/エンドポイントへのGETリクエスト
- **597-615行目**: エラーハンドリング（401/403判定）
- **617-624行目**: トークン欠落チェック
- **627-632行目**: トークンのプロファイル保存

#### Step 4: エラー表示を理解する

エラーメッセージの表示処理を追う。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | login.dart | `lib/settings/login.dart` | エラー表示部分（60-71行目、92-103行目） |
| 4-2 | dialogs.dart | `lib/widget/dialogs.dart` | statusCodeToString関数 |

**主要処理フロー**:
- **60-71行目**: レスポンスからエラーメッセージ抽出
- **94-103行目**: エラーメッセージのListTile表示

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

```
InvenTreeLoginWidget
    │
    └─ _doLogin(context)
           │
           ├─ formKey.currentState.validate()
           │
           ├─ showLoadingOverlay()
           │
           ├─ InvenTreeAPI().fetchToken(profile, username, password)
           │      │
           │      ├─ getDeviceInfo()
           │      │
           │      ├─ get("user/token/", headers: BasicAuth)
           │      │      └─ apiRequest() → completeRequest()
           │      │
           │      └─ UserProfileDBManager().updateProfile(profile)
           │
           ├─ hideLoadingOverlay()
           │
           └─ [成功] Navigator.pop()
              [失敗] setState(error)
```

### データフロー図

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

username ───▶ InvenTreeLoginWidget ───▶ APIRequest
password            │                         │
    │               ├─ バリデーション            │
    │               └─ Basic認証ヘッダー生成     │
    │                                          │
    └───────────────────────────────────────────┘
                         │
                    fetchToken
                         │
                         ▼
                  [APIレスポンス]
                         │
            ┌────────────┴────────────┐
            ▼                         ▼
    [成功: token取得]           [失敗: error表示]
            │
            ▼
    UserProfile.token
            │
            ▼
    sembast DB保存
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| login.dart | `lib/settings/login.dart` | ソース | ログイン画面UI |
| api.dart | `lib/api.dart` | ソース | fetchTokenメソッド（トークン取得API） |
| user_profile.dart | `lib/user_profile.dart` | ソース | UserProfileモデル、トークン保存 |
| progress.dart | `lib/widget/progress.dart` | ソース | ローディングオーバーレイ |
| dialogs.dart | `lib/widget/dialogs.dart` | ソース | ステータスコードメッセージ変換 |
| helpers.dart | `lib/helpers.dart` | ソース | デバイス情報取得（getDeviceInfo） |
