管理者ログイン

概要説明

管理者ログイン機能は、特に昇格された権限を持つ管理ユーザー向けの認証を提供します。標準的なユーザーログインと類似のフローですが、管理者ロールの検証と拡張されたユーザー読み込みの追加チェックが含まれています。この機能により、適切な管理者権限を持つユーザーのみがアプリケーションの管理セクションにアクセスできるようになり、通常のユーザーアクセスと管理機能の間に明確な分離が作られます。

アクティビティ図

---
config:
  theme: base
  layout: dagre
  flowchart:
    curve: linear
    htmlLabels: true
  themeVariables:
    edgeLabelBackground: "transparent"
---
flowchart TD
    %% Main components
    AdminUser[管理者ユーザー]
    FirebaseService[Firebase認証]
    Database[(データベース)]
    
    %% Process steps with numbering
    AdminUser --- Step1[
        <div style='text-align: center'>
            <span style='display: inline-block; background-color: #6699cc !important; color:white; width: 28px; height: 28px; line-height: 28px; border-radius: 50%; font-weight: bold'>1</span>
            <p style='margin-top: 8px'>ログイン送信</p>
        </div>
    ]
    Step1 --> ValidateRequest[リクエスト検証]
    
    ValidateRequest --- Step2[
        <div style='text-align: center'>
            <span style='display: inline-block; background-color: #6699cc !important; color:white; width: 28px; height: 28px; line-height: 28px; border-radius: 50%; font-weight: bold'>2</span>
            <p style='margin-top: 8px'>トークン抽出</p>
        </div>
    ]
    Step2 --> ExtractToken[Firebaseトークン抽出]
    
    ExtractToken --- Step3[
        <div style='text-align: center'>
            <span style='display: inline-block; background-color: #6699cc !important; color:white; width: 28px; height: 28px; line-height: 28px; border-radius: 50%; font-weight: bold'>3</span>
            <p style='margin-top: 8px'>認証サービスに送信</p>
        </div>
    ]
    Step3 --> AuthService[認証サービス]
    
    AuthService --- Step4[
        <div style='text-align: center'>
            <span style='display: inline-block; background-color: #6699cc !important; color:white; width: 28px; height: 28px; line-height: 28px; border-radius: 50%; font-weight: bold'>4</span>
            <p style='margin-top: 8px'>トークン検証</p>
        </div>
    ]
    Step4 --> FirebaseService
    
    FirebaseService --- Step5[
        <div style='text-align: center'>
            <span style='display: inline-block; background-color: #6699cc !important; color:white; width: 28px; height: 28px; line-height: 28px; border-radius: 50%; font-weight: bold'>5</span>
            <p style='margin-top: 8px'>トークン検証</p>
        </div>
    ]
    Step5 --> TokenCheck{有効なトークン?}
    
    TokenCheck --- Step6A[
        <div style='text-align: center'>
            <span style='display: inline-block; background-color: #cc6666 !important; color:white; width: 28px; height: 28px; line-height: 28px; border-radius: 50%; font-weight: bold'>6A</span>
            <p style='margin-top: 8px'>エラー返却</p>
        </div>
    ]
    Step6A -->|いいえ| ReturnError[エラー返却]
    
    TokenCheck --- Step6B[
        <div style='text-align: center'>
            <span style='display: inline-block; background-color: #6699cc !important; color:white; width: 28px; height: 28px; line-height: 28px; border-radius: 50%; font-weight: bold'>6B</span>
            <p style='margin-top: 8px'>ユーザー検索</p>
        </div>
    ]
    Step6B -->|はい| FindUser[データベースでユーザー検索]
    
    FindUser --- Step7[
        <div style='text-align: center'>
            <span style='display: inline-block; background-color: #6699cc !important; color:white; width: 28px; height: 28px; line-height: 28px; border-radius: 50%; font-weight: bold'>7</span>
            <p style='margin-top: 8px'>存在確認</p>
        </div>
    ]
    Step7 --> UserCheck{ユーザー存在?}
    
    UserCheck --- Step8A[
        <div style='text-align: center'>
            <span style='display: inline-block; background-color: #cc6666 !important; color:white; width: 28px; height: 28px; line-height: 28px; border-radius: 50%; font-weight: bold'>8A</span>
            <p style='margin-top: 8px'>エラー返却</p>
        </div>
    ]
    Step8A -->|いいえ| ReturnError
    
    UserCheck --- Step8B[
        <div style='text-align: center'>
            <span style='display: inline-block; background-color: #6699cc !important; color:white; width: 28px; height: 28px; line-height: 28px; border-radius: 50%; font-weight: bold'>8B</span>
            <p style='margin-top: 8px'>ロール確認</p>
        </div>
    ]
    Step8B -->|はい| CheckAdminRole[管理者ロール確認]
    
    CheckAdminRole --- Step9[
        <div style='text-align: center'>
            <span style='display: inline-block; background-color: #6699cc !important; color:white; width: 28px; height: 28px; line-height: 28px; border-radius: 50%; font-weight: bold'>9</span>
            <p style='margin-top: 8px'>ロール検証</p>
        </div>
    ]
    Step9 --> RoleCheck{管理者ロールあり?}
    
    RoleCheck --- Step10A[
        <div style='text-align: center'>
            <span style='display: inline-block; background-color: #cc6666 !important; color:white; width: 28px; height: 28px; line-height: 28px; border-radius: 50%; font-weight: bold'>10A</span>
            <p style='margin-top: 8px'>エラー返却</p>
        </div>
    ]
    Step10A -->|いいえ| ReturnError
    
    RoleCheck --- Step10B[
        <div style='text-align: center'>
            <span style='display: inline-block; background-color: #99cc66 !important; color:white; width: 28px; height: 28px; line-height: 28px; border-radius: 50%; font-weight: bold'>10B</span>
            <p style='margin-top: 8px'>Cookie生成</p>
        </div>
    ]
    Step10B -->|はい| GenerateCookies[認証Cookie生成]
    
    GenerateCookies --- Step11[
        <div style='text-align: center'>
            <span style='display: inline-block; background-color: #99cc66 !important; color:white; width: 28px; height: 28px; line-height: 28px; border-radius: 50%; font-weight: bold'>11</span>
            <p style='margin-top: 8px'>データ返却</p>
        </div>
    ]
    Step11 --> ReturnUserData[管理者ユーザーデータ返却]
    
    ReturnUserData --- Step12[
        <div style='text-align: center'>
            <span style='display: inline-block; background-color: #99cc66 !important; color:white; width: 28px; height: 28px; line-height: 28px; border-radius: 50%; font-weight: bold'>12</span>
            <p style='margin-top: 8px'>ログイン完了</p>
        </div>
    ]
    Step12 --> AdminUser
    
    %% Styling
    style AdminUser fill:#e6f3ff,stroke:#0066cc,stroke-width:2px
    style ValidateRequest fill:#f0f8e6,stroke:#339933,stroke-width:2px
    style ExtractToken fill:#f0f8e6,stroke:#339933,stroke-width:2px
    style AuthService fill:#f0f8e6,stroke:#339933,stroke-width:2px
    style FirebaseService fill:#fcd9d9,stroke:#cc3333,stroke-width:2px
    style TokenCheck fill:#f5f0ff,stroke:#9966cc,stroke-width:2px
    style FindUser fill:#f0f8e6,stroke:#339933,stroke-width:2px
    style UserCheck fill:#f5f0ff,stroke:#9966cc,stroke-width:2px
    style CheckAdminRole fill:#f0f8e6,stroke:#339933,stroke-width:2px
    style RoleCheck fill:#f5f0ff,stroke:#9966cc,stroke-width:2px
    style GenerateCookies fill:#f0f8e6,stroke:#339933,stroke-width:2px
    style ReturnUserData fill:#f0f8e6,stroke:#339933,stroke-width:2px
    style ReturnError fill:#fcd9d9,stroke:#cc3333,stroke-width:2px
    style Database fill:#ffe6cc,stroke:#ff9900,stroke-width:2px
    
    style Step1 fill:transparent,stroke:transparent,stroke-width:1px
    style Step2 fill:transparent,stroke:transparent,stroke-width:1px
    style Step3 fill:transparent,stroke:transparent,stroke-width:1px
    style Step4 fill:transparent,stroke:transparent,stroke-width:1px
    style Step5 fill:transparent,stroke:transparent,stroke-width:1px
    style Step6A fill:transparent,stroke:transparent,stroke-width:1px
    style Step6B fill:transparent,stroke:transparent,stroke-width:1px
    style Step7 fill:transparent,stroke:transparent,stroke-width:1px
    style Step8A fill:transparent,stroke:transparent,stroke-width:1px
    style Step8B fill:transparent,stroke:transparent,stroke-width:1px
    style Step9 fill:transparent,stroke:transparent,stroke-width:1px
    style Step10A fill:transparent,stroke:transparent,stroke-width:1px
    style Step10B fill:transparent,stroke:transparent,stroke-width:1px
    style Step11 fill:transparent,stroke:transparent,stroke-width:1px
    style Step12 fill:transparent,stroke:transparent,stroke-width:1px

API: 管理者ログインAPI

ケースドキュメント

ケース1: 管理者ログイン成功

説明

管理者が有効な認証情報と管理者ロールで正常にログインします。

シーケンス図

sequenceDiagram
    participant Admin as 管理者
    participant API as ログインコントローラー
    participant Service as 認証サービス
    participant Firebase
    participant DB as データベース

    Note over Admin,API: ステップ1: ログイン送信
    Admin->>API: POST /api/v1/admin/auth/login (firebase-tokenあり)
    
    Note over API,Service: ステップ2: ログイン処理
    API->>Service: login(token, data)
    
    Note over Service,Firebase: ステップ3: トークン検証
    Service->>Firebase: verifyIdToken(token)
    Firebase-->>Service: 検証済みユーザー情報の返却
    
    Note over Service,DB: ステップ4: ユーザー検索
    Service->>DB: findByUid(uid)
    DB-->>Service: ユーザーデータの返却
    
    Note over Service,DB: ステップ5: 管理者ロール確認
    Service->>DB: 管理者ロールの確認
    DB-->>Service: ロールデータの返却
    
    Note over Service,DB: ステップ6: グループデータ読込
    Service->>DB: グループデータの読込
    DB-->>Service: グループデータの返却
    
    Note over Service,DB: ステップ7: グループメンバー読込
    Service->>DB: グループメンバーの読込
    DB-->>Service: グループメンバーデータの返却
    
    Note over Service,DB: ステップ8: グループロール読込
    Service->>DB: グループロールの読込
    DB-->>Service: グループロールデータの返却
    
    Note over Service,API: ステップ9: 認証Cookie生成
    Service->>Service: createAuthCookie(user)
    
    Note over API,Admin: ステップ10: レスポンス返却
    API-->>Admin: 200 OK (ユーザーデータとCookie)

ステップ

ステップ1: ログインリクエスト送信

  • 説明: 管理者がFirebaseトークン付きでログインリクエストを送信
  • リクエスト: POST /api/v1/admin/auth/login
  • ヘッダー:
    • firebase-token: Firebase認証トークン
  • 検証:
    • トークン存在チェック
    • トークンフォーマット検証

ステップ2: ログイン処理

  • 説明: コントローラーがリクエストを検証しサービスに渡す
  • アクション:
    • ヘッダーからトークンを抽出
    • 認証サービスを呼び出し

ステップ3: トークン検証

  • 説明: Firebaseトークンの信頼性を検証
  • アクション:
    • 検証のためトークンをFirebaseに送信
    • 検証済みトークンからユーザー識別子を抽出

ステップ4: ユーザー検索

  • 説明: ユーザーレコードを検索し管理者権限を確認
  • アクション:
    • Firebase UIDに一致するユーザーをデータベースに問い合わせ
    • ユーザーの管理者ロールリレーションをロード
    • ユーザーに管理者ロールが割り当てられているか確認

ステップ5: 管理者ロール確認

  • 説明: ユーザーが適切な管理者権限を持っていることを確認
  • アクション:
    • 管理者ロールが存在し有効であることを確認
    • 特定のロール権限が必要かどうかを確認

ステップ6: グループデータ読込

  • 説明: ユーザーに関連するグループデータを読み込む
  • アクション:
    • ユーザーのグループメンバーシップに関するグループデータベースを照会
    • 各グループに関連するグループデータをロード

ステップ7: グループメンバー読込

  • 説明: ユーザーに関連するグループメンバーデータを読み込む
  • アクション:
    • ユーザーのメンバーシップ詳細に関するグループメンバーデータベースを照会
    • 各グループに関連するグループメンバーデータをロード

ステップ8: グループロール読込

  • 説明: ユーザーに関連するグループロールデータを読み込む
  • アクション:
    • 各グループでのユーザーのロールに関するグループロールデータベースを照会
    • 各グループに関連するグループロールデータをロード

ステップ9: 認証Cookie生成

  • 説明: 安全な管理者セッションCookieを作成
  • アクション:
    • 認証トークンの生成
    • トークンを使用したCookieの作成: Trend-Viewer_auth_api_token
    • ログイン状態Cookieの作成: Trend-Viewer_is_logged_in

ステップ10: レスポンス返却

  • 説明: クライアントに成功レスポンスを送信
  • レスポンス:
    • 成功: 200 OK (管理者ユーザーデータ付き)
    • レスポンスにCookieを設定
    • ロール情報を含むユーザー詳細を含める

関連するデータベーステーブルとフィールド

erDiagram
    users {
        id bigint "主キー"
        name string "ユーザーのフルネーム"
        email string "ユーザーのメールアドレス (ユニーク)"
        uid string "Firebase UID"
        status int "アカウントステータス"
        is_first_login boolean "ユーザーが初回ログインを完了したかのフラグ"
        payment_provider_customer_id string "決済プロバイダーの顧客ID"
        show_free_plan_modal boolean "無料プランモーダル表示フラグ"
        group_id bigint "groupsテーブルへの参照"
        created_at timestamp "レコード作成タイムスタンプ"
        updated_at timestamp "レコード最終更新タイムスタンプ"
    }
    groups {
        id bigint "主キー"
        name string "グループ名"
        created_at timestamp "レコード作成タイムスタンプ"
        updated_at timestamp "レコード最終更新タイムスタンプ"
    }
    group_members {
        id bigint "主キー"
        user_id bigint "usersテーブルへの参照"
        group_id bigint "groupsテーブルへの参照"
        group_role_id bigint "group_rolesテーブルへの参照"
        created_at timestamp "レコード作成タイムスタンプ"
        updated_at timestamp "レコード最終更新タイムスタンプ"
    }
    group_roles {
        id bigint "主キー"
        name string "ロール名"
        slug string "ロールスラッグ"
        created_at timestamp "レコード作成タイムスタンプ"
        updated_at timestamp "レコード最終更新タイムスタンプ"
    }
    admin_roles {
        id bigint "主キー"
        name string "ロール名"
        slug string "ロールスラッグ"
        created_at timestamp "レコード作成タイムスタンプ"
        updated_at timestamp "レコード最終更新タイムスタンプ"
    }
    admin_role_user {
        user_id bigint "usersテーブルへの参照"
        admin_role_id bigint "admin_rolesテーブルへの参照"
        created_at timestamp "レコード作成タイムスタンプ"
        updated_at timestamp "レコード最終更新タイムスタンプ"
    }

    users ||--o{ group_members : has
    groups ||--o{ group_members : has
    group_roles ||--o{ group_members : has
    users ||--o{ admin_role_user : has
    admin_roles ||--o{ admin_role_user : has

エラー処理

  • ログ

    • ログイン失敗はアプリケーションログに記録
    • ロール検証エラーを記録
    • (オプション) セキュリティイベントのSlackメッセージ送信
  • エラー詳細:

    ステータスコード エラーメッセージ 説明
    401 "ログイン情報が正しくありません。" 管理者ロールにユーザーが見つからない場合
    401 "認証情報と一致するレコードがありません。" ログインに失敗した場合
    401 "問題が発生しました。申し訳ございませんが、もう一度お試しください。" 予期しないエラーが発生した場合

ケース2: 管理者ログイン失敗 (管理者ロールなし)

説明

有効な認証情報を持つユーザーがログインを試みるが、管理者ロールを持っていない。

シーケンス図

sequenceDiagram
    participant User as ユーザー
    participant API as 管理者ログインコントローラー
    participant Service as 認証サービス
    participant Firebase
    participant DB as データベース

    User->>API: POST /api/v1/admin/auth/login (firebase-tokenあり)
    API->>Service: login(token, data)
    Service->>Firebase: verifyIdToken(token)
    Firebase-->>Service: 検証済みトークンデータの返却
    Service->>DB: findByUid(firebase_uid)
    DB-->>Service: ユーザーデータの返却
    Service->>DB: 管理者ロール読込
    DB-->>Service: 空のロール返却
    Service-->>API: authInfoとユーザーを返却
    API->>API: ユーザーが管理者ロールを持つか確認
    API-->>User: 401 未認証 (管理者ロールなし)

ステップ

ステップ1: ログインリクエスト送信

  • 説明: ユーザーが有効なFirebaseトークンでログインリクエストを送信
  • リクエスト: 成功フローと同じ

ステップ2: トークン検証成功

  • 説明: Firebaseがトークンを検証しユーザーが見つかる
  • アクション: ロールチェックまで成功フローと同じ

ステップ3: 管理者ロールチェック失敗

  • 説明: システムが管理者ロールを確認するが見つからない
  • アクション:
    • adminRoleリレーションをチェック
    • ユーザーに必要な権限がないことを判断

ステップ4: エラーレスポンス

  • 説明: ユーザーに適切なエラーを返す
  • レスポンス: 権限に関するエラーメッセージ付きの401 未認証

追加事項

  • 管理者ログインは通常のユーザーログインと認証インフラを共有している
  • 管理者ロールチェックは通常のログインには存在しない追加レイヤー
  • ログイン後に利用可能な機能はロールベースのアクセス制御によって決定される
  • 管理者ユーザーは異なる権限レベルを持つ複数のロールを持つことがある
  • 管理者セッションは通常のユーザーセッションと同じセキュリティプロパティを持つ