Đặt lại mật khẩu
Mô tả tổng quan
Tính năng Đặt lại mật khẩu cho phép người dùng khôi phục quyền truy cập vào tài khoản khi họ quên mật khẩu. Quy trình này bao gồm việc yêu cầu liên kết đặt lại mật khẩu, nhận email với token được bảo mật và sử dụng token đó để đặt mật khẩu mới. Hệ thống đảm bảo bảo mật bằng cách tạo token có thời hạn, xác minh quyền sở hữu email và yêu cầu tạo mật khẩu mới an toàn đáp ứng yêu cầu của hệ thống.
API: Password Reset API
Sơ đồ hoạt động
---
config:
theme: base
layout: dagre
flowchart:
curve: linear
htmlLabels: true
themeVariables:
edgeLabelBackground: "transparent"
---
flowchart TB
%% Main components
Client[Ứng dụng Client]
ForgotPasswordController[ForgotPasswordController]
ResetPasswordController[ResetPasswordController]
AuthService(AuthService)
EmailService((EmailService))
Firebase((Firebase Auth))
UserDB[(users)]
ResetTokenDB[(password_resets)]
Client --- 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 mật khẩu</p>
</div>
]
Step1 --> ForgotPasswordController
ForgotPasswordController --- 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'>Xác thực yêu cầu</p>
</div>
]
Step2 --> AuthService
AuthService --- 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'>Kiểm tra User tồn tại</p>
</div>
]
Step3 --> UserDB
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'>Tạo Reset Token</p>
</div>
]
Step4 --> ResetTokenDB
AuthService --- 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 Reset Email</p>
</div>
]
Step5 --> EmailService
EmailService --- 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ề Response</p>
</div>
]
Step6 --> ForgotPasswordController
ForgotPasswordController --- 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'>Trả về Response</p>
</div>
]
Step7 --> Client
Client --- 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'>Gửi form đặt lại</p>
</div>
]
Step8 --> ResetPasswordController
ResetPasswordController --- 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 Token</p>
</div>
]
Step9 --> AuthService
AuthService --- 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'>Cập nhật mật khẩu</p>
</div>
]
Step10 --> Firebase
AuthService --- 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'>Xóa Token</p>
</div>
]
Step11 --> ResetTokenDB
AuthService --- 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ề Response</p>
</div>
]
Step12 --> ResetPasswordController
ResetPasswordController --- Step13[
<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'>13</span>
<p style='margin-top: 8px'>Trả về Response</p>
</div>
]
Step13 --> Client
%% Styling
style Client fill:#e6f3ff,stroke:#0066cc,stroke-width:2px
style ForgotPasswordController fill:#e6f3ff,stroke:#0066cc,stroke-width:2px
style ResetPasswordController fill:#e6f3ff,stroke:#0066cc,stroke-width:2px
style AuthService fill:#f0f8e6,stroke:#339933,stroke-width:2px
style EmailService fill:#fcd9d9,stroke:#cc3333,stroke-width:2px
style Firebase fill:#fcd9d9,stroke:#cc3333,stroke-width:2px
style UserDB fill:#ffe6cc,stroke:#ff9900,stroke-width:2px
style ResetTokenDB 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 Step9 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
style Step13 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
Mô tả
Người dùng yêu cầu liên kết đặt lại mật khẩu bằng cách cung cấp địa chỉ email của họ.
Sơ đồ tuần tự
sequenceDiagram
participant User
participant API as ForgotPasswordController
participant Service as AuthService
participant Email as EmailService
participant UserDB as Database
participant TokenDB as Database
Note over User,API: Bước 1: Gửi yêu cầu đặt lại
User->>API: POST /api/v1/general/auth/forgot (với email và URL)
Note over API,Service: Bước 2: Xử lý yêu cầu
API->>Service: sendResetPassEmail(email, url)
Note over Service,UserDB: Bước 3: Kiểm tra User
Service->>UserDB: findByEmail(email)
UserDB-->>Service: Trả về dữ liệu user
Note over Service,TokenDB: Bước 4: Tạo Reset Token
Service->>TokenDB: createToken(email)
TokenDB-->>Service: Trả về reset token
Note over Service,Email: Bước 5: Gửi Reset Email
Service->>Email: sendPasswordResetLink(email, token, url)
Email-->>Service: Trạng thái gửi email
Note over API,User: Bước 6: Trả về Response
API-->>User: 200 OK với thông báo thành công
Các bước
Bước 1: Gửi yêu cầu đặt lại
- Mô tả: Người dùng gửi địa chỉ email để đặt lại mật khẩu
- Request:
POST /api/v1/general/auth/forgot - Body Parameters:
- email: Địa chỉ email đã đăng ký của người dùng
- url: URL phía client cho form đặt lại mật khẩu
- Validation:
- Xác thực định dạng email
- Validation các trường bắt buộc
- Kiểm tra giới hạn tốc độ (3 yêu cầu mỗi giờ)
Bước 2: Xử lý yêu cầu
- Mô tả: Controller chuyển yêu cầu đã xác thực cho service
- Hành động:
- Trích xuất email và reset URL
- Gọi authentication service cho quy trình đặt lại mật khẩu
- Ghi log yêu cầu đặt lại
Bước 3: Kiểm tra User
- Mô tả: Xác minh user tồn tại trong hệ thống
- Hành động:
- Truy vấn database cho user với email được cung cấp
- Xác minh tài khoản user đang hoạt động
- Kiểm tra nếu user có Firebase authentication
Bước 4: Tạo Reset Token
- Mô tả: Tạo token an toàn để đặt lại mật khẩu
- Hành động:
- Tạo token bảo mật về mặt mật mã
- Lưu token với thời gian hết hạn (60 phút)
- Liên kết token với email user
- Xóa bất kỳ token hiện có nào cho email này
Bước 5: Gửi Reset Email
- Mô tả: Gửi hướng dẫn đặt lại mật khẩu qua email
- Hành động:
- Tạo email với hướng dẫn đặt lại
- Bao gồm reset URL với token
- Đặt ưu tiên email cao
- Gửi email đến địa chỉ của user
- Ghi log trạng thái gửi email
Bước 6: Trả về Response
- Mô tả: Thông báo cho user về trạng thái gửi email
- Response:
- Thành công:
200 OKvới thông báo thành công - Lỗi: Mã lỗi phù hợp với thông báo
- Thành công:
Bảng dữ liệu liên quan
erDiagram
users {
bigint id PK "Khóa chính tự động tăng"
string name "Tên đầy đủ của người dùng"
string email "Địa chỉ email của người dùng (duy nhất)"
string uid "Firebase user ID (duy nhất)"
string payment_provider_customer_id "Stripe customer id bắt đầu với 'cus_' (có thể null)"
integer status "Trạng thái tài khoản: 0: Không hoạt động, 1: Hoạt động"
integer is_first_login "1: đã đăng nhập, 0: chưa đăng nhập"
string remember_token "Sử dụng cho chức năng 'remember me' (có thể null)"
}
password_resets {
string email "Địa chỉ email của người dùng (index)"
string token "Reset token (đã hash)"
timestamp expires_at "Thời gian hết hạn token"
}
users ||--o{ password_resets : yêu cầu
Xử lý lỗi
-
Ghi log
- Lỗi gửi email được ghi log
- Vấn đề tạo token được ghi lại
- (Tùy chọn) Gửi slack message 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 "メールアドレスが見つかりません。" Khi email không được tìm thấy 400 "メールの送信に失敗しました。" Khi gửi email thất bại 429 "リクエストが多すぎます。" Khi vượt quá giới hạn tốc độ 400 Generic error với exception message Khi xảy ra lỗi không mong muốn
Trường hợp 2: Đặt lại mật khẩu với Token
Mô tả
Người dùng đặt lại mật khẩu của họ sử dụng token nhận được qua email.
Sơ đồ tuần tự
sequenceDiagram
participant User
participant API as ResetPasswordController
participant Service as AuthService
participant TokenDB as Database
participant Firebase
User->>API: POST /api/v1/general/auth/reset
API->>Service: resetPassword(token, email, password)
Service->>TokenDB: validateToken(token, email)
TokenDB-->>Service: Kết quả xác minh token
Service->>Firebase: updatePassword(email, password)
Firebase-->>Service: Trạng thái cập nhật mật khẩu
Service->>TokenDB: deleteToken(token, email)
Service-->>API: Kết quả đặt lại
API-->>User: 200 OK với thông báo thành công
Các bước
Bước 1: Gửi form đặt lại
- Mô tả: Người dùng gửi mật khẩu mới với reset token
- Request:
POST /api/v1/general/auth/reset - Body Parameters:
- token: Reset token từ email
- email: Địa chỉ email của người dùng
- password: Mật khẩu mới
- password_confirmation: Xác nhận mật khẩu
- Validation:
- Sự hiện diện và định dạng token
- Độ mạnh mật khẩu và xác nhận
- Email khớp với token
- Kiểm tra giới hạn tốc độ (3 lần thử mỗi giờ)
Bước 2: Xác minh Token
- Mô tả: Xác minh tính hợp lệ và thời gian hết hạn của token
- Hành động:
- Kiểm tra token tồn tại trong database
- Xác minh token chưa hết hạn
- Xác nhận token khớp với email được cung cấp
- Ghi log lần thử xác minh
Bước 3: Cập nhật mật khẩu
- Mô tả: Đặt mật khẩu mới cho user
- Hành động:
- Cập nhật mật khẩu trong Firebase authentication
- Áp dụng hash mật khẩu theo tiêu chuẩn bảo mật
- Ghi log cập nhật mật khẩu
Bước 4: Xóa Token
- Mô tả: Loại bỏ token đã sử dụng khỏi database
- Hành động:
- Xóa bản ghi token để ngăn tái sử dụng
- Dọn dẹp bất kỳ token hết hạn nào
- Ghi log xóa token
Bước 5: Trả về Response
- Mô tả: Thông báo cho user về thành công đặt lại mật khẩu
- Response:
- Thành công:
200 OKvới thông báo thành công - Lỗi: Mã lỗi phù hợp với thông báo chi tiết
- Thành công:
Xử lý lỗi
-
Ghi log
- Lỗi xác minh token
- Lỗi cập nhật mật khẩu
- (Tùy chọn) Gửi slack message 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 "無効なトークンです。" Khi token không hợp lệ hoặc hết hạn 400 "パスワードリセットに失敗しました。" Khi đặt lại mật khẩu thất bại 429 "リクエストが多すぎます。" Khi vượt quá giới hạn tốc độ 400 Generic error với exception message Khi xảy ra lỗi không mong muốn
Ghi chú bổ sung
- Giới hạn tốc độ: Các lần thử đặt lại bị giới hạn 3 lần mỗi giờ cho mỗi địa chỉ IP
- Reset tokens hết hạn sau 60 phút
- Hệ thống ngăn tái sử dụng token bằng cách xóa tokens sau khi đặt lại mật khẩu thành công
- Yêu cầu mật khẩu:
- Tối thiểu 8 ký tự
- Ít nhất một chữ cái viết hoa
- Ít nhất một chữ cái viết thường
- Ít nhất một số
- Ít nhất một ký tự đặc biệt
- Reset URL được cung cấp bởi client application để cho phép các triển khai front-end khác nhau
- Xem xét triển khai các biện pháp bảo mật bổ sung:
- Giới hạn tốc độ dựa trên IP
- Khóa tài khoản sau nhiều lần thử thất bại
- Thông báo email khi thay đổi mật khẩu thành công