Đăng nhập Admin

Mô tả Tổng quan

Tính năng Đăng nhập Admin cung cấp xác thực đặc biệt cho người dùng quản trị với quyền hạn cao hơn. Nó tuân theo luồng tương tự như đăng nhập user tiêu chuẩn nhưng bao gồm các kiểm tra bổ sung cho việc xác minh vai trò admin và tải user mở rộng. Tính năng này đảm bảo rằng chỉ những user có quyền admin phù hợp mới có thể truy cập các phần quản trị của ứng dụng, tạo ra sự phân tách rõ ràng giữa quyền truy cập user thông thường và khả năng quản trị.

Biểu đồ Hoạt động

---
config:
  theme: base
  layout: dagre
  flowchart:
    curve: linear
    htmlLabels: true
  themeVariables:
    edgeLabelBackground: "transparent"
---
flowchart TD
    %% Main components
    AdminUser[Admin User]
    FirebaseService[Firebase Authentication]
    Database[(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'>Gửi Đăng nhập</p>
        </div>
    ]
    Step1 --> ValidateRequest[Xác thực Yêu cầu]
    
    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'>Trích xuất Token</p>
        </div>
    ]
    Step2 --> ExtractToken[Trích xuất Firebase Token]
    
    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'>Gửi đến Auth Service</p>
        </div>
    ]
    Step3 --> AuthService[Auth Service]
    
    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'>Xác minh Token</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'>Kiểm tra Token</p>
        </div>
    ]
    Step5 --> TokenCheck{Token Hợp lệ?}
    
    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'>Trả về Lỗi</p>
        </div>
    ]
    Step6A -->|Không| ReturnError[Trả về Lỗi]
    
    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'>Tìm User</p>
        </div>
    ]
    Step6B -->|Có| FindUser[Tìm User trong Database]
    
    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'>Kiểm tra Sự tồn tại</p>
        </div>
    ]
    Step7 --> UserCheck{User Tồn tại?}
    
    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'>Trả về Lỗi</p>
        </div>
    ]
    Step8A -->|Không| 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'>Kiểm tra Vai trò</p>
        </div>
    ]
    Step8B -->|Có| CheckRole[Kiểm tra Vai trò Admin]
    
    CheckRole --- 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'>Xác minh Quyền</p>
        </div>
    ]
    Step9 --> RoleCheck{Admin Role?}
    
    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'>Trả về Lỗi</p>
        </div>
    ]
    Step10A -->|Không| ReturnError
    
    RoleCheck --- Step10B[
        <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'>10B</span>
            <p style='margin-top: 8px'>Tải Dữ liệu</p>
        </div>
    ]
    Step10B -->|Có| LoadData[Tải Dữ liệu User & Nhóm]
    
    LoadData --- Step11[
        <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'>11</span>
            <p style='margin-top: 8px'>Tạo Cookie</p>
        </div>
    ]
    Step11 --> CreateCookie[Tạo Auth Cookie]
    
    CreateCookie --- Step12[
        <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'>12</span>
            <p style='margin-top: 8px'>Trả về Phản hồi</p>
        </div>
    ]
    Step12 --> Success[Trả về Thành công]
    
    %% Styling
    style AdminUser fill:#e6f3ff,stroke:#0066cc,stroke-width:2px
    style FirebaseService fill:#fcd9d9,stroke:#cc3333,stroke-width:2px
    style Database fill:#ffe6cc,stroke:#ff9900,stroke-width:2px
    style AuthService fill:#f0f8e6,stroke:#339933,stroke-width:2px
    style ValidateRequest fill:#e6f3ff,stroke:#0066cc,stroke-width:2px
    style ExtractToken fill:#e6f3ff,stroke:#0066cc,stroke-width:2px
    style FindUser fill:#f0f8e6,stroke:#339933,stroke-width:2px
    style CheckRole fill:#f0f8e6,stroke:#339933,stroke-width:2px
    style LoadData fill:#f0f8e6,stroke:#339933,stroke-width:2px
    style CreateCookie fill:#f0f8e6,stroke:#339933,stroke-width:2px
    style Success fill:#e6f3ff,stroke:#0066cc,stroke-width:2px
    style ReturnError fill:#fcd9d9,stroke:#cc3333,stroke-width:2px
    style TokenCheck fill:#ffe6cc,stroke:#ff9900,stroke-width:2px
    style UserCheck fill:#ffe6cc,stroke:#ff9900,stroke-width:2px
    style RoleCheck 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

Tài liệu Trường hợp

Trường hợp 1: Đăng nhập Thành công

Biểu đồ Tuần tự

sequenceDiagram
    participant Admin
    participant API as AuthController
    participant Service as AuthService
    participant Firebase
    participant UserDB as users
    participant AdminRoleDB as admin_roles
    participant AdminRoleUserDB as admin_role_user
    participant GroupDB as groups
    participant GroupMemberDB as group_members
    participant GroupRoleDB as group_roles

    Note over Admin,API: Bước 1: Gửi Đăng nhập
    Admin->>API: POST /api/v1/admin/auth/login (với firebase-token)
    
    Note over API,Service: Bước 2: Xử lý Đăng nhập
    API->>Service: login(token, data)
    
    Note over Service,Firebase: Bước 3: Xác minh Token
    Service->>Firebase: verifyIdToken(token)
    Firebase-->>Service: Trả về thông tin user đã xác minh
    
    Note over Service,UserDB: Bước 4: Tìm User
    Service->>UserDB: findByUid(uid)
    UserDB-->>Service: Trả về dữ liệu user
    
    Note over Service,AdminRoleUserDB: Bước 5: Kiểm tra Vai trò Admin
    Service->>AdminRoleUserDB: Kiểm tra Vai trò Admin
    AdminRoleUserDB-->>Service: Trả về dữ liệu vai trò
    
    Note over Service,GroupDB: Bước 6: Tải Dữ liệu Nhóm
    Service->>GroupDB: Tải dữ liệu nhóm
    GroupDB-->>Service: Trả về dữ liệu nhóm
    
    Note over Service,GroupMemberDB: Bước 7: Tải Thành viên Nhóm
    Service->>GroupMemberDB: Tải thành viên nhóm
    GroupMemberDB-->>Service: Trả về dữ liệu thành viên nhóm
    
    Note over Service,GroupRoleDB: Bước 8: Tải Vai trò Nhóm
    Service->>GroupRoleDB: Tải vai trò nhóm
    GroupRoleDB-->>Service: Trả về dữ liệu vai trò nhóm
    
    Note over Service,API: Bước 9: Tạo Auth Cookie
    Service->>Service: createAuthCookie(user)
    
    Note over API,Admin: Bước 10: Trả về Phản hồi
    API-->>Admin: 200 OK với dữ liệu user và cookies

Trường hợp 2: Đăng nhập Thất bại

Biểu đồ Tuần tự

sequenceDiagram
    participant Admin
    participant API as AuthController
    participant Service as AuthService
    participant Firebase
    participant UserDB as users
    participant AdminRoleDB as admin_roles

    Note over Admin,API: Bước 1: Gửi Đăng nhập
    Admin->>API: POST /api/v1/admin/auth/login (với firebase-token)
    
    Note over API,Service: Bước 2: Xử lý Đăng nhập
    API->>Service: login(token, data)
    
    Note over Service,Firebase: Bước 3: Xác minh Token
    Service->>Firebase: verifyIdToken(token)
    Firebase-->>Service: Trả về lỗi xác minh
    
    Note over Service,API: Bước 4: Xử lý Lỗi
    Service->>API: Trả về lỗi xác minh
    
    Note over API,Admin: Bước 5: Trả về Phản hồi Lỗi
    API-->>Admin: 401 Unauthorized với thông báo lỗi

Các Bước

Bước 1: Gửi Đăng nhập

  • Mô tả: Admin gửi thông tin đăng nhập
  • Yêu cầu: POST /api/v1/admin/auth/login
  • Headers:
    • firebase-token: Token từ xác thực Firebase
  • Xác thực:
    • Kiểm tra sự hiện diện của token
    • Xác thực định dạng token
    • Kiểm tra giới hạn tốc độ (5 lần/phút)

Bước 2: Xử lý Đăng nhập

  • Mô tả: Controller xác thực yêu cầu và chuyển cho service
  • Hành động:
    • Trích xuất token từ header
    • Gọi service xác thực cho quá trình đăng nhập
    • Ghi log lần thử đăng nhập

Bước 3: Xác minh Token

  • Mô tả: Xác minh tính xác thực của Firebase token
  • Hành động:
    • Gửi token đến Firebase để xác minh
    • Trích xuất thông tin user từ token đã xác minh
    • Xác nhận nhà cung cấp xác thực

Bước 4: Tìm User

  • Mô tả: Tìm bản ghi user trong cơ sở dữ liệu
  • Hành động:
    • Truy vấn cơ sở dữ liệu cho user với Firebase UID khớp
    • Xác minh user tồn tại và đang hoạt động
    • Tải các mối quan hệ của user

Bước 5: Kiểm tra Vai trò Admin

  • Mô tả: Xác minh user có quyền admin
  • Hành động:
    • Kiểm tra các vai trò được gán cho user
    • Xác minh vai trò admin có mặt
    • Xác thực quyền của vai trò

Bước 6: Tải Dữ liệu Nhóm

  • Mô tả: Lấy dữ liệu liên quan đến nhóm
  • Hành động:
    • Truy vấn cơ sở dữ liệu cho dữ liệu nhóm
    • Xác minh sự tồn tại của nhóm
    • Tải các mối quan hệ của nhóm

Bước 7: Tải Thành viên Nhóm

  • Mô tả: Lấy dữ liệu thành viên nhóm
  • Hành động:
    • Truy vấn cơ sở dữ liệu cho dữ liệu thành viên nhóm
    • Xác minh sự tồn tại của thành viên nhóm
    • Tải các mối quan hệ của thành viên nhóm

Bước 8: Tải Vai trò Nhóm

  • Mô tả: Lấy dữ liệu vai trò nhóm
  • Hành động:
    • Truy vấn cơ sở dữ liệu cho dữ liệu vai trò nhóm
    • Xác minh sự tồn tại của vai trò nhóm
    • Tải các mối quan hệ của vai trò nhóm

Bước 9: Tạo Auth Cookie

  • Mô tả: Tạo cookie phiên làm việc an toàn
  • Hành động:
    • Tạo token xác thực
    • Tạo auth cookie với token: Trend-Viewer_auth_api_token
    • Tạo cookie trạng thái đăng nhập: Trend-Viewer_is_logged_in
    • Đặt cờ secure và httpOnly
    • Đặt domain và path phù hợp

Bước 10: Trả về Phản hồi

  • Mô tả: Gửi phản hồi thành công cho client
  • Phản hồi:
    • Thành công: 200 OK với dữ liệu user
    • Đặt cookies trong phản hồi
    • Bao gồm chi tiết user trong body phản hồi

API Endpoint

Endpoint: POST /api/v1/admin/auth/login

Headers:

  • firebase-token: Token xác thực từ Firebase (bắt buộc)

Body Parameters:

  • Không có (tất cả thông tin xác thực được truyền qua header)

Response:

{
    "success": true,
    "message": "Đăng nhập thành công",
    "data": {
        "user": {
            "id": 1,
            "name": "Admin User",
            "email": "admin@example.com",
            "uid": "firebase_uid_here",
            "status": 1,
            "is_first_login": false,
            "created_at": "2024-01-01T00:00:00.000000Z",
            "updated_at": "2024-01-01T00:00:00.000000Z"
        },
        "admin_roles": [
            {
                "id": 1,
                "name": "Super Admin",
                "slug": "super_admin"
            }
        ],
        "groups": [
            {
                "id": 1,
                "name": "Default Group",
                "group_members": [...],
                "group_roles": [...]
            }
        ]
    }
}

Bảng Cơ sở Dữ liệu Liên quan & Trường

erDiagram
    users {
        bigint id PK
        string name "Tên đầy đủ của user"
        string email "Địa chỉ email của user (duy nhất)"
        string uid "Firebase user ID (duy nhất)"
        int status "Trạng thái tài khoản: 1: hoạt động, 0: không hoạt động"
        boolean is_first_login "Cờ cho biết user đã hoàn thành đăng nhập lần đầu"
        timestamp created_at
        timestamp updated_at
    }
    roles {
        bigint id PK
        string name "Tên hiển thị vai trò"
        string slug "Định danh vai trò cho quyền (duy nhất)"
        timestamp created_at
        timestamp updated_at
    }
    admin_role_user {
        bigint user_id FK "Tham chiếu đến bảng users"
        bigint role_id FK "Tham chiếu đến bảng roles"
        timestamp created_at
        timestamp updated_at
    }
    groups {
        bigint id PK
        string name "Tên nhóm"
        string description "Mô tả nhóm"
        int status "Trạng thái nhóm: 1: hoạt động, 0: không hoạt động"
        timestamp created_at
        timestamp updated_at
    }
    group_members {
        bigint id PK
        bigint group_id FK "Tham chiếu đến bảng groups"
        bigint user_id FK "Tham chiếu đến bảng users"
        int status "Trạng thái thành viên: 1: hoạt động, 0: không hoạt động"
        timestamp created_at
        timestamp updated_at
    }
    group_roles {
        bigint id PK
        bigint group_id FK "Tham chiếu đến bảng groups"
        bigint user_id FK "Tham chiếu đến bảng users"
        string role "Vai trò trong nhóm"
        timestamp created_at
        timestamp updated_at
    }

    users ||--o{ admin_role_user : has
    roles ||--o{ admin_role_user : has
    users ||--o{ group_members : belongs_to
    groups ||--o{ group_members : contains
    users ||--o{ group_roles : has_role_in
    groups ||--o{ group_roles : assigns_role

Xử lý Lỗi

  • Ghi log

    • Các lần đăng nhập thất bại được ghi vào log ứng dụng
    • Lỗi xác minh vai trò được ghi lại
    • (Tùy chọn) Gửi tin nhắn slack cho các sự kiện bảo mật
  • Chi tiết Lỗi:

    Mã Trạng thái Thông báo Lỗi Mô tả
    401 "Thông tin đăng nhập không chính xác." Khi thông tin đăng nhập không hợp lệ
    403 "Không có quyền quản trị." Khi user thiếu vai trò admin
    429 "Quá nhiều yêu cầu." Khi vượt quá giới hạn tốc độ
    401 Lỗi chung với thông báo exception Khi xảy ra lỗi không mong đợi

Ghi chú Bổ sung

  • Giới hạn tốc độ: Các lần thử đăng nhập admin bị giới hạn ở 5 lần/phút cho mỗi địa chỉ IP
  • Thời gian hết hạn phiên làm việc: Phiên làm việc admin hết hạn sau 8 giờ không hoạt động
  • Bảo mật: Hệ thống sử dụng Firebase authentication để đảm bảo tính bảo mật cao
  • Kiểm tra vai trò: Chỉ những user có vai trò admin mới có thể truy cập
  • Ghi log: Tất cả các lần đăng nhập admin đều được ghi lại để kiểm tra bảo mật
  • Cân nhắc triển khai:
    • Xác thực hai yếu tố cho quyền truy cập admin
    • Hạn chế truy cập dựa trên IP
    • Giám sát hoạt động và cảnh báo
    • Theo dõi hoạt động phiên làm việc