Quản lý Mật khẩu Admin

Mô tả Tổng quan

Tính năng Quản lý Mật khẩu Admin cung cấp khả năng đặt lại mật khẩu cho tài khoản quản trị một cách an toàn. Tính năng này bao gồm hai quy trình chính: yêu cầu đặt lại mật khẩu (gửi email với link đặt lại) và thực hiện đặt lại mật khẩu (sử dụng token xác minh). Hệ thống đảm bảo tính bảo mật cao bằng cách sử dụng token có thời gian hết hạn ngắn và ghi lại tất cả các hoạt động liên quan đến mật khẩu.

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]
    PasswordController[PasswordController]
    PasswordService[PasswordService]
    EmailService[Email Service]
    TokenDB[(Token Store)]
    UserDB[(User 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'>Yêu cầu Đặt lại</p>
        </div>
    ]
    Step1 --> ValidateEmail[Xác thực Email]
    
    ValidateEmail --- 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'>Kiểm tra User</p>
        </div>
    ]
    Step2 --> CheckUser[Kiểm tra User trong Database]
    
    CheckUser --- 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'>Tạo Token</p>
        </div>
    ]
    Step3 --> GenerateToken[Tạo Token Đặt lại]
    
    GenerateToken --- 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'>Lưu Token</p>
        </div>
    ]
    Step4 --> StoreToken[Lưu Token vào Database]
    
    StoreToken --- 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'>Gửi Email</p>
        </div>
    ]
    Step5 --> SendEmail[Gửi Email với Link]
    
    SendEmail --- Step6[
        <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'>6</span>
            <p style='margin-top: 8px'>Trả về Phản hồi</p>
        </div>
    ]
    Step6 --> Success[Trả về Thành công]
    
    %% Reset Password Flow
    AdminUser --- 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'>Nhập Mật khẩu Mới</p>
        </div>
    ]
    Step7 --> ValidateToken[Xác thực Token]
    
    ValidateToken --- Step8[
        <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'>8</span>
            <p style='margin-top: 8px'>Kiểm tra Hợp lệ</p>
        </div>
    ]
    Step8 --> TokenValid{Token Hợp lệ?}
    
    TokenValid --- Step9A[
        <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'>9A</span>
            <p style='margin-top: 8px'>Trả về Lỗi</p>
        </div>
    ]
    Step9A -->|Không| ReturnError[Trả về Lỗi]
    
    TokenValid --- Step9B[
        <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'>9B</span>
            <p style='margin-top: 8px'>Cập nhật Mật khẩu</p>
        </div>
    ]
    Step9B -->|Có| UpdatePassword[Cập nhật Mật khẩu]
    
    UpdatePassword --- Step10[
        <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'>10</span>
            <p style='margin-top: 8px'>Xóa Token</p>
        </div>
    ]
    Step10 --> ClearToken[Xóa Token đã sử dụng]
    
    ClearToken --- 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'>Ghi log</p>
        </div>
    ]
    Step11 --> LogReset[Ghi log Đặt lại Mật khẩu]
    
    LogReset --- 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 --> ResetSuccess[Trả về Thành công]
    
    %% Styling
    style AdminUser fill:#e6f3ff,stroke:#0066cc,stroke-width:2px
    style PasswordController fill:#e6f3ff,stroke:#0066cc,stroke-width:2px
    style PasswordService fill:#f0f8e6,stroke:#339933,stroke-width:2px
    style EmailService fill:#fcd9d9,stroke:#cc3333,stroke-width:2px
    style TokenDB fill:#ffe6cc,stroke:#ff9900,stroke-width:2px
    style UserDB fill:#ffe6cc,stroke:#ff9900,stroke-width:2px
    style ValidateEmail fill:#e6f3ff,stroke:#0066cc,stroke-width:2px
    style CheckUser fill:#f0f8e6,stroke:#339933,stroke-width:2px
    style GenerateToken fill:#f0f8e6,stroke:#339933,stroke-width:2px
    style StoreToken fill:#f0f8e6,stroke:#339933,stroke-width:2px
    style SendEmail fill:#f0f8e6,stroke:#339933,stroke-width:2px
    style Success fill:#e6f3ff,stroke:#0066cc,stroke-width:2px
    style ValidateToken fill:#f0f8e6,stroke:#339933,stroke-width:2px
    style UpdatePassword fill:#f0f8e6,stroke:#339933,stroke-width:2px
    style ClearToken fill:#f0f8e6,stroke:#339933,stroke-width:2px
    style LogReset fill:#f0f8e6,stroke:#339933,stroke-width:2px
    style ResetSuccess fill:#e6f3ff,stroke:#0066cc,stroke-width:2px
    style ReturnError fill:#fcd9d9,stroke:#cc3333,stroke-width:2px
    style TokenValid 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 Step6 fill:transparent,stroke:transparent,stroke-width:1px
    style Step7 fill:transparent,stroke:transparent,stroke-width:1px
    style Step8 fill:transparent,stroke:transparent,stroke-width:1px
    style Step9A fill:transparent,stroke:transparent,stroke-width:1px
    style Step9B fill:transparent,stroke:transparent,stroke-width:1px
    style Step10 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: Yêu cầu Đặt lại Mật khẩu

Biểu đồ Tuần tự

sequenceDiagram
    participant Admin
    participant API as PasswordController
    participant Service as PasswordService
    participant UserDB as Database
    participant TokenDB as Database
    participant EmailService as Email

    Note over Admin,API: Bước 1: Gửi Yêu cầu Đặt lại
    Admin->>API: POST /api/v1/admin/auth/forgot (với email)
    
    Note over API,Service: Bước 2: Xử lý Yêu cầu
    API->>Service: sendResetLink(email)
    
    Note over Service,UserDB: Bước 3: Tìm User
    Service->>UserDB: findByEmail(email)
    UserDB-->>Service: Trả về dữ liệu user
    
    Note over Service,Service: Bước 4: Tạo Token
    Service->>Service: generateResetToken()
    
    Note over Service,TokenDB: Bước 5: Lưu Token
    Service->>TokenDB: storeResetToken(user_id, token)
    TokenDB-->>Service: Xác nhận lưu
    
    Note over Service,EmailService: Bước 6: Gửi Email
    Service->>EmailService: sendResetEmail(email, token)
    EmailService-->>Service: Xác nhận gửi
    
    Note over API,Admin: Bước 7: Trả về Phản hồi
    API-->>Admin: 200 OK với thông báo gửi email

Trường hợp 2: Đặt lại Mật khẩu với Token

Biểu đồ Tuần tự

sequenceDiagram
    participant Admin
    participant API as PasswordController
    participant Service as PasswordService
    participant TokenDB as Database
    participant UserDB as Database

    Note over Admin,API: Bước 1: Gửi Token và Mật khẩu Mới
    Admin->>API: POST /api/v1/admin/auth/reset (với token, password)
    
    Note over API,Service: Bước 2: Xử lý Đặt lại
    API->>Service: resetPassword(token, password)
    
    Note over Service,TokenDB: Bước 3: Xác minh Token
    Service->>TokenDB: validateResetToken(token)
    TokenDB-->>Service: Trả về thông tin token
    
    Note over Service,Service: Bước 4: Kiểm tra Hợp lệ
    Service->>Service: validateToken(token_info)
    
    Note over Service,UserDB: Bước 5: Cập nhật Mật khẩu
    Service->>UserDB: updatePassword(user_id, password)
    UserDB-->>Service: Xác nhận cập nhật
    
    Note over Service,TokenDB: Bước 6: Xóa Token
    Service->>TokenDB: deleteResetToken(token)
    TokenDB-->>Service: Xác nhận xóa
    
    Note over API,Admin: Bước 7: Trả về Phản hồi
    API-->>Admin: 200 OK với thông báo đặt lại thành công

Các Bước

Quy trình Yêu cầu Đặt lại Mật khẩu

Bước 1: Gửi Yêu cầu Đặt lại

  • Mô tả: Admin gửi yêu cầu đặt lại mật khẩu
  • Yêu cầu: POST /api/v1/admin/auth/forgot
  • Body Parameters:
    • email: Địa chỉ email của tài khoản admin (bắt buộc)
  • Xác thực:
    • Kiểm tra định dạng email
    • Kiểm tra giới hạn tốc độ (3 lần/phút)

Bước 2: Xử lý Yêu cầu

  • Mô tả: Controller xác thực yêu cầu và chuyển cho service
  • Hành động:
    • Xác thực định dạng email
    • Gọi service mật khẩu cho quá trình đặt lại
    • Ghi log yêu cầu đặt lại

Bước 3: 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 email khớp
    • Xác minh user tồn tại và có vai trò admin
    • Kiểm tra trạng thái tài khoản

Bước 4: Tạo Token

  • Mô tả: Tạo token đặt lại mật khẩu an toàn
  • Hành động:
    • Tạo token ngẫu nhiên 64 ký tự
    • Đặt thời gian hết hạn (1 giờ)
    • Mã hóa token trước khi lưu

Bước 5: Lưu Token

  • Mô tả: Lưu token vào cơ sở dữ liệu
  • Hành động:
    • Lưu token đã mã hóa
    • Liên kết với user_id
    • Đặt thời gian hết hạn

Bước 6: Gửi Email

  • Mô tả: Gửi email với link đặt lại mật khẩu
  • Hành động:
    • Tạo link đặt lại với token
    • Gửi email đến địa chỉ đã đăng ký
    • Ghi log việc gửi email

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

  • Mô tả: Gửi phản hồi thành công
  • Phản hồi:
    • Thành công: 200 OK với thông báo gửi email
    • Không tiết lộ thông tin về sự tồn tại của email

Quy trình Đặt lại Mật khẩu

Bước 1: Gửi Token và Mật khẩu Mới

  • Mô tả: Admin gửi token và mật khẩu mới
  • Yêu cầu: POST /api/v1/admin/auth/reset
  • Body Parameters:
    • token: Token đặt lại mật khẩu (bắt buộc)
    • password: Mật khẩu mới (bắt buộc)
    • password_confirmation: Xác nhận mật khẩu (bắt buộc)

Bước 2: Xử lý Đặt lại

  • Mô tả: Controller xác thực yêu cầu và chuyển cho service
  • Hành động:
    • Xác thực mật khẩu mới
    • Gọi service mật khẩu cho quá trình đặt lại
    • Ghi log yêu cầu đặt lại

Bước 3: Xác minh Token

  • Mô tả: Xác minh tính hợp lệ của token
  • Hành động:
    • Tìm token trong cơ sở dữ liệu
    • Kiểm tra thời gian hết hạn
    • Xác minh token chưa được sử dụng

Bước 4: Kiểm tra Hợp lệ

  • Mô tả: Kiểm tra các điều kiện hợp lệ
  • Hành động:
    • Kiểm tra token còn hiệu lực
    • Xác thực mật khẩu mới đáp ứng yêu cầu
    • Kiểm tra mật khẩu xác nhận khớp

Bước 5: Cập nhật Mật khẩu

  • Mô tả: Cập nhật mật khẩu mới cho user
  • Hành động:
    • Mã hóa mật khẩu mới
    • Cập nhật trong cơ sở dữ liệu
    • Ghi log thay đổi mật khẩu

Bước 6: Xóa Token

  • Mô tả: Xóa token đã sử dụng
  • Hành động:
    • Xóa token khỏi cơ sở dữ liệu
    • Đảm bảo token không thể sử dụng lại
    • Ghi log việc xóa token

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

  • Mô tả: Gửi phản hồi thành công
  • Phản hồi:
    • Thành công: 200 OK với thông báo đặt lại thành công
    • Xác nhận mật khẩu đã được thay đổi

API Endpoints

1. Yêu cầu Đặt lại Mật khẩu

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

Headers:

  • Content-Type: application/json

Body Parameters:

{
    "email": "admin@example.com"
}

Response:

{
    "success": true,
    "message": "Email đặt lại mật khẩu đã được gửi",
    "data": {
        "email_sent": true,
        "expires_in": "1 giờ"
    }
}

2. Đặt lại Mật khẩu

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

Headers:

  • Content-Type: application/json

Body Parameters:

{
    "token": "reset_token_here",
    "password": "new_password123",
    "password_confirmation": "new_password123"
}

Response:

{
    "success": true,
    "message": "Mật khẩu đã được đặt lại thành công",
    "data": {
        "password_updated": true,
        "updated_at": "2024-01-01T08:00:00.000000Z"
    }
}

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)"
        string password "Mật khẩu đã mã hóa (cho admin)"
        int status "Trạng thái tài khoản: 1: hoạt động, 0: không hoạt động"
        timestamp created_at
        timestamp updated_at
    }
    password_resets {
        bigint id PK
        string email "Địa chỉ email của user"
        string token "Token đặt lại mật khẩu đã mã hóa"
        timestamp expires_at "Thời gian hết hạn token"
        boolean is_used "Trạng thái sử dụng: 0: chưa sử dụng, 1: đã sử dụng"
        timestamp created_at
        timestamp updated_at
    }
    password_change_logs {
        bigint id PK
        bigint user_id FK "Tham chiếu đến bảng users"
        string change_type "Loại thay đổi: reset, update"
        string ip_address "Địa chỉ IP của user"
        timestamp changed_at "Thời gian thay đổi"
        timestamp created_at
    }

    users ||--o{ password_change_logs : logs
    password_resets ||--o{ users : belongs_to

Xử lý Lỗi

  • Ghi log

    • Tất cả các yêu cầu đặt lại mật khẩu được ghi vào log ứng dụng
    • Lỗi xác minh token đượ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ả
    400 "Email không hợp lệ." Khi định dạng email không đúng
    404 "Email không tồn tại." Khi email không có trong hệ thống
    429 "Quá nhiều yêu cầu." Khi vượt quá giới hạn tốc độ
    400 "Token không hợp lệ hoặc đã hết hạn." Khi token không hợp lệ
    400 "Mật khẩu xác nhận không khớp." Khi mật khẩu xác nhận sai
    422 "Mật khẩu không đáp ứng yêu cầu." Khi mật khẩu quá yếu
    500 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 yêu cầu đặt lại mật khẩu bị giới hạn ở 3 lần/phút cho mỗi địa chỉ IP
  • Thời gian hết hạn token: Token đặt lại mật khẩu hết hạn sau 1 giờ
  • Bảo mật: Token được mã hóa trước khi lưu vào cơ sở dữ liệu
  • Email: Hệ thống gửi email với link đặt lại mật khẩu an toàn
  • Ghi log: Tất cả các hoạt động liên quan đến mật khẩu đều được ghi lại
  • Yêu cầu mật khẩu: Mật khẩu mới phải đáp ứng các yêu cầu bảo mật
  • Cân nhắc triển khai:
    • Gửi email thông báo khi mật khẩu được thay đổi
    • Yêu cầu đăng nhập lại sau khi đặt lại mật khẩu
    • Theo dõi các lần thay đổi mật khẩu bất thường
    • Triển khai xác thực hai yếu tố cho admin