Thiết kế Kỹ thuật Phần mềm
Module Xác thực
AUTH
1. Tổng quan
1.1 Mục đích
Tài liệu này mô tả các quyết định thiết kế kỹ thuật cho module Authentication.
1.2 Yêu cầu được Giải quyết
| Danh mục | Mã Yêu cầu | Phần Thiết kế |
| Chức năng | FR-001 - FR-020 | Phần 3, 5 |
| Phi chức năng | NFR-001 - NFR-007 | Phần 6 |
2. Kiến trúc
2.1 Kiểu Kiến trúc
| Khía cạnh | Quyết định | Giải quyết |
| Mẫu | NestJS Module (Monolith) | NFR-007 |
| Thư viện Xác thực | Better Auth | FR-001 - FR-009 |
| ORM | Drizzle ORM | Tất cả yêu cầu dữ liệu |
| Cơ sở dữ liệu | PostgreSQL (auth_db) | NFR-006 |
| Chiến lược Token | JWT (access + refresh) | NFR-004 |
2.2 Các Quyết định Thiết kế Quan trọng
| # | Quyết định | Lý do | Giải quyết |
| 1 | Sử dụng Better Auth |
Xử lý quản lý phiên, tạo token, xác minh thông tin đăng nhập |
FR-001 - FR-009 |
| 2 | PostgreSQL |
Đảm bảo ACID, hỗ trợ JSON, hệ sinh thái trưởng thành |
NFR-006 |
| 3 | JWT access + refresh tokens |
Truy cập không trạng thái cho hiệu suất, refresh cho bảo mật |
NFR-004 |
| 4 | bcrypt mã hóa mật khẩu |
Tiêu chuẩn ngành, chi phí thích ứng |
NFR-003 |
| 5 | Redis cho giới hạn tốc độ |
Tra cứu nhanh, hỗ trợ TTL |
NFR-005 |
3. Thiết kế Thành phần
3.1 Sơ đồ Thành phần
Hình 1: Các Thành phần Module AUTH
3.2 Cấu trúc Module
src/
auth/
auth.module.ts
auth.controller.ts
auth.service.ts
auth.guard.ts
dto/
users/
users.module.ts
users.controller.ts
users.service.ts
roles/
roles.module.ts
roles.controller.ts
roles.service.ts
organizations/
organizations.module.ts
organizations.controller.ts
organizations.service.ts
teams/
teams.module.ts
teams.controller.ts
teams.service.ts
invitations/
invitations.module.ts
invitations.controller.ts
invitations.service.ts
3.3 Ánh xạ Thành phần đến Yêu cầu
| Thành phần | Trách nhiệm | Giải quyết |
| AuthController | Đăng nhập, đăng ký, đăng xuất, refresh, đặt lại mật khẩu | FR-001 - FR-009 |
| AuthService | Tạo token, mã hóa mật khẩu, xác minh | NFR-003, NFR-004 |
| UsersController | Thao tác CRUD người dùng | FR-010 |
| RolesController | Quản lý vai trò, phân quyền | FR-011 |
| SocialAuthController | Đăng nhập mạng xã hội, liên kết/gỡ liên kết tài khoản | FR-012 |
| SessionController | Liệt kê phiên, thu hồi, giả mạo | FR-013, FR-014 |
| OrganizationsController | CRUD tổ chức, thành viên, vai trò | FR-015 |
| TeamsController | CRUD nhóm, quản lý thành viên | FR-016 |
| InvitationsController | Mời, chấp nhận, từ chối, thu hồi, gửi lại, xác minh lời mời | FR-017, FR-018, FR-019, FR-020 |
| Email Queue Worker | Consume RabbitMQ events (verification, password reset), gọi EMAIL module API | FR-001, FR-005, FR-008 |
| SMS Queue Worker | Consume RabbitMQ events (verification, password reset), gọi SMS module API | FR-006, FR-009 |
| AuthGuard | Xác minh JWT, bảo vệ tuyến đường | NFR-004 |
4. Kiến trúc Dữ liệu
4.1 Cơ sở dữ liệu
Cơ sở dữ liệu: auth_db (PostgreSQL)
Xem thêm: Thiết kế Cơ sở dữ liệu
4.2 Giải quyết Yêu cầu Dữ liệu
| Yêu cầu SRS | Giải pháp Thiết kế | Bảng DB |
| FR-001 Đăng ký | Chèn bản ghi người dùng + tài khoản | auth_users, auth_accounts |
| FR-002 Đăng nhập | Truy vấn người dùng, xác minh mật khẩu, tạo phiên | auth_users, auth_sessions |
| FR-008/FR-009 Xác minh | Lưu mã xác minh, cập nhật trạng thái người dùng | auth_verification, auth_users |
| FR-011 Vai trò | RBAC với vai trò và quyền hạn | auth_roles, auth_user_roles |
5. Thiết kế API
5.1 Ánh xạ Endpoint đến Yêu cầu
| Phương thức | Đường dẫn | Mô tả | Giải quyết |
| POST | /sign-up/email | Đăng ký người dùng mới | FR-001 |
| POST | /sign-in/email | Đăng nhập bằng email | FR-002 |
| POST | /sign-in/social | Đăng nhập mạng xã hội | FR-012 |
| POST | /sign-out | Đăng xuất | FR-003 |
| POST | /refresh-token | Làm mới token | FR-004 |
| POST | /request-password-reset | Yêu cầu đặt lại mật khẩu | FR-005 |
| POST | /change-password | Đổi mật khẩu | FR-007 |
| GET | /verify-email | Xác minh email | FR-008 |
| GET | /list-sessions | Liệt kê các phiên | FR-013 |
| POST | /revoke-session | Thu hồi phiên | FR-013 |
| POST | /link-social | Liên kết tài khoản mạng xã hội | FR-012 |
| POST | /admin/set-role | Đặt vai trò người dùng cấp hệ thống | FR-011 |
| POST | /admin/ban-user | Cấm người dùng | FR-014 |
| POST | /admin/impersonate-user | Giả mạo người dùng | FR-014 |
| POST | /organization/create | Tạo tổ chức | FR-015 |
| POST | /organization/create-role | Tạo vai trò tùy chỉnh tổ chức | FR-015 |
| POST | /organization/update-role | Cập nhật vai trò tổ chức | FR-015 |
| POST | /organization/delete-role | Xóa vai trò tổ chức | FR-015 |
| GET | /organization/list-roles | Liệt kê các vai trò tổ chức | FR-015 |
| GET | /organization/get-role | Lấy vai trò tổ chức | FR-015 |
| POST | /organization/has-permission | Kiểm tra quyền tổ chức | FR-015 |
| POST | /organization/invite-member | Mời thành viên (tạo token, gửi email) | FR-017 |
| GET | /organization/verify-invitation | Xác minh token lời mời (công khai, không cần xác thực) | FR-020 |
| POST | /organization/accept-invitation | Chấp nhận lời mời qua token | FR-017 |
| POST | /organization/reject-invitation | Từ chối lời mời | FR-017 |
| POST | /organization/resend-invitation | Gửi lại lời mời (token mới) | FR-018 |
| DELETE | /organization/cancel-invitation | Thu hồi/hủy lời mời (chỉ chủ sở hữu) | FR-019 |
| POST | /organization/create-team | Tạo nhóm | FR-016 |
6. Luồng Tích hợp
6.1 Luồng Đăng nhập
Sơ đồ Hoạt động
Hình 1: Luồng Hoạt động Đăng nhập
Sơ đồ Chuỗi
Hình 2: Luồng Tích hợp Đăng nhập
6.2 Luồng Đăng ký
Sơ đồ Hoạt động
Hình 3: Luồng Hoạt động Đăng ký
Sơ đồ Chuỗi
Hình 4: Luồng Tích hợp Đăng ký
Chi tiết Đăng ký
Endpoint đăng ký mặc định gán vai trò hệ thống user:
| Vai trò Hệ thống | Mô tả | Tự Đăng ký |
user | Tài khoản người dùng tiêu chuẩn (mặc định). Có thể tạo tổ chức và nhận các vai trò cấp tổ chức. | Có (mặc định) |
super_admin | Quản trị viên hệ thống có toàn quyền truy cập. | Không (chỉ tạo bởi admin qua POST /admin/create-user) |
Lưu ý: Các vai trò cấp tổ chức (HR, Candidate, Employer) được định nghĩa theo từng tổ chức và gán cho thành viên thông qua hệ thống vai trò tổ chức. Chúng không phải là vai trò cấp hệ thống.
Luồng Email Xác minh qua RabbitMQ
Sau khi tạo tài khoản thành công, AUTH publish sự kiện lên RabbitMQ để gửi email xác minh. Email queue worker chạy bên trong AUTH service xử lý bất đồng bộ:
| Bước | Thành phần | Hành động |
| 1 | AUTH Service | Tạo tài khoản người dùng, gán vai trò user |
| 2 | AUTH Service | Generate mã xác minh (OTP/token), lưu vào auth_verification |
| 3 | AUTH Service | Publish sự kiện verification.email.requested lên RabbitMQ (queue: verification.email) |
| 4 | Email Queue Worker (AUTH) | Consume sự kiện từ queue |
| 5 | Email Queue Worker (AUTH) | Gọi EMAIL module API để gửi email xác minh |
| 6 | EMAIL Module | Gửi email qua SendGrid/SMTP, lưu log vào email_logs |
| 7 | EMAIL Module | Gửi webhook callback verification.email.delivered về AUTH |
Event Payload:
{
"user_id": "uuid",
"email": "[email protected]",
"channel": "email",
"otp": "123456",
"verification_id": "ver-uuid"
}
Xử lý lỗi:
- EMAIL module không khả dụng → Event vẫn được publish, worker retry với exponential backoff (tối đa 5 lần)
- Gửi email thất bại → Ghi log lỗi, user có thể yêu cầu gửi lại qua
POST /send-verification-email
6.3 Luồng Đặt lại Mật khẩu
Sơ đồ Hoạt động
Hình 5: Luồng Hoạt động Đặt lại Mật khẩu
Sơ đồ Chuỗi
Hình 6: Luồng Tích hợp Đặt lại Mật khẩu
Đặt lại Mật khẩu qua Email
Luồng đặt lại mật khẩu qua email sử dụng cùng kiến trúc event-driven với xác minh email:
| Bước | Thành phần | Hành động |
| 1 | AUTH Service | Nhận yêu cầu từ POST /request-password-reset |
| 2 | AUTH Service | Kiểm tra email tồn tại trong auth_users (không tiết lộ nếu email không tồn tại) |
| 3 | AUTH Service | Tạo reset token, lưu hash vào auth_verification với TTL 1 giờ |
| 4 | AUTH Service | Publish sự kiện password.reset.email.requested lên RabbitMQ (queue: password.reset.email) |
| 5 | Email Queue Worker (AUTH) | Consume sự kiện từ queue |
| 6 | Email Queue Worker (AUTH) | Gọi EMAIL module API để gửi email chứa liên kết đặt lại |
| 7 | EMAIL Module | Gửi email qua SendGrid/SMTP, lưu log vào email_logs |
| 8 | Client | User nhấp link → GET /reset-password/{token} → Nhập mật khẩu mới |
| 9 | AUTH Service | Xác minh token, cập nhật mật khẩu trong auth_users |
| 10 | AUTH Service | Hủy tất cả phiên của user trong auth_sessions |
Event Payload:
{
"user_id": "uuid",
"email": "[email protected]",
"reset_token": "reset-token-abc",
"expires_at": "2026-08-05T11:30:00Z",
"verification_id": "ver-uuid"
}
Đặt lại Mật khẩu qua SMS
| Bước | Thành phần | Hành động |
| 1 | AUTH Service | Nhận yêu cầu từ POST /request-password-reset với phone number |
| 2 | AUTH Service | Kiểm tra SĐT tồn tại (không tiết lộ nếu SĐT không tồn tại) |
| 3 | AUTH Service | Tạo mã OTP (6 chữ số), lưu vào auth_verification với TTL 5 phút |
| 4 | AUTH Service | Publish sự kiện password.reset.sms.requested lên RabbitMQ (queue: password.reset.sms) |
| 5 | SMS Queue Worker (AUTH) | Consume sự kiện từ queue |
| 6 | SMS Queue Worker (AUTH) | Gọi SMS module API để gửi OTP |
| 7 | SMS Module | Gửi SMS qua Twilio, lưu log vào sms_logs |
| 8 | Client | User nhập OTP + mật khẩu mới → POST /reset-password |
| 9 | AUTH Service | Xác minh OTP, cập nhật mật khẩu |
| 10 | AUTH Service | Hủy tất cả phiên |
Xử lý lỗi:
- EMAIL/SMS module không khả dụng → Event vẫn được publish, worker retry exponential backoff (tối đa 5 lần)
- Gửi thất bại → User có thể yêu cầu gửi lại qua
POST /request-password-reset
- Token/OTP hết hạn → System trả về 400, user yêu cầu gửi lại
6.4 Luồng Đăng xuất
Sơ đồ Hoạt động
Hình 7: Luồng Hoạt động Đăng xuất
Sơ đồ Chuỗi
Hình 8: Luồng Tích hợp Đăng xuất
6.5 Luồng Xác minh Tài khoản
Sơ đồ Hoạt động
Hình 9: Luồng Hoạt động Xác minh Tài khoản
Sơ đồ Chuỗi
Hình 10: Luồng Tích hợp Xác minh Tài khoản
6.6 Luồng Lời mời
Sơ đồ Hoạt động
Xem Phần 4.17 UC-17: Quản lý Lời mời trong SRS để xem luồng hoạt động lời mời.
Sơ đồ Chuỗi
Luồng lời mời bao gồm AUTH → EMAIL (bất đồng bộ qua RabbitMQ) → Frontend → AUTH (xác minh/chấp nhận).
- Admin gọi POST /organization/invite-member → AUTH tạo token, lưu hash, phát sự kiện
- Module EMAIL tiêu thụ sự kiện, gửi email lời mời với token gốc trong URL
- Người được mời nhấp liên kết → Frontend gọi GET /organization/verify-invitation?token=... (công khai)
- AUTH hash token, xác minh, trả về trạng thái
- Người được mời chấp nhận → POST /organization/accept-invitation → AUTH cập nhật trạng thái, phát sự kiện
Thiết kế Bảo mật Token
| Khía cạnh | Triển khai |
| Tạo Token | crypto.randomBytes(32).toString('hex') — chuỗi hex 64 ký tự |
| Lưu trữ | Chỉ lưu hash SHA-256 trong cột token_hash (ràng buộc UNIQUE) |
| Gửi đi | Token gốc trong URL: {FRONTEND_DOMAIN}/invitations/verify?token={rawToken} |
| Hết hạn | 72 giờ (có thể cấu hình qua INVITATION_EXPIRY_HOURS) |
| Gửi lại | Tạo token mới, vô hiệu hóa token cũ, đặt lại thời hạn |
| Thu hồi | Chỉ chủ sở hữu; trạng thái đặt thành REVOKED; token không còn hiệu lực |
Vòng đời Trạng thái Lời mời
PENDING → ACCEPTED (kết thúc)
PENDING → EXPIRED → PENDING (qua gửi lại)
PENDING → REVOKED → PENDING (qua gửi lại)
7. Thiết kế Bảo mật
| Yêu cầu SRS | Giải pháp Thiết kế | Triển khai |
| NFR-003 Mã hóa mật khẩu |
bcrypt với 12 vòng |
bcrypt.hash(password, 12) |
| NFR-004 Thời hạn token |
Access ngắn hạn + refresh dài hạn |
Access: 15 phút, Refresh: 7 ngày |
| NFR-005 Giới hạn tốc độ |
Bộ giới hạn tốc độ dựa trên Redis |
10 req/min cho các endpoint xác thực |
8. Tổng kết Truy xuất nguồn gốc
| Mã Yêu cầu SRS | Yêu cầu | Phần TDS | Quyết định Thiết kế |
| FR-001 | Đăng ký Người dùng | 3.2, 5.1, 6.2 | AuthController + POST /auth/register → RabbitMQ (verification.email) → Email Queue Worker → EMAIL module |
| FR-002 | Đăng nhập Người dùng | 2.2, 5.1 | Better Auth + JWT + POST /auth/login |
| FR-003 | Đăng xuất | 5.1 | POST /auth/logout (xóa phiên) |
| FR-004 | Làm mới Token | 2.2, 5.1 | JWT refresh + POST /auth/refresh |
| FR-005 | Đặt lại Mật khẩu (Email) | 5.1, 6.3 | POST /auth/forgot-password → RabbitMQ (password.reset.email) → Email Queue Worker → EMAIL module |
| FR-006 | Đặt lại Mật khẩu (Điện thoại) | 5.1, 6.3 | POST /auth/forgot-password-phone → RabbitMQ (password.reset.sms) → SMS Queue Worker → SMS module |
| FR-007 | Đổi Mật khẩu | 5.1 | POST /auth/change-password |
| FR-008 | Xác minh Email | 5.1 | GET /auth/verify-email |
| FR-009 | Xác minh Điện thoại | 5.1 | POST /auth/verify-phone |
| FR-010 | Quản lý Người dùng | 3.2, 5.1 | UsersController + GET/PUT/DELETE /users/:id |
| FR-011 | Quản lý Vai trò | 3.2, 5.1 | RolesController + POST /admin/set-role |
| FR-012 | Xác thực Mạng xã hội | 3.2, 5.1 | SocialAuthController + POST /sign-in/social |
| FR-013 | Quản lý Phiên | 3.2, 5.1 | SessionController + GET /list-sessions |
| FR-014 | Cấm & Giả mạo Người dùng | 3.2, 5.1 | SessionController + POST /admin/ban-user |
| FR-015 | Quản lý Tổ chức | 3.2, 5.1 | OrganizationsController + POST /organization/create |
| FR-016 | Quản lý Nhóm | 3.2, 5.1 | TeamsController + POST /organization/create-team |
| FR-017 | Quản lý Lời mời | 3.2, 5.1 | InvitationsController + POST /organization/invite-member |
| FR-018 | Gửi lại Lời mời | 3.2, 5.1 | InvitationsController + POST /organization/resend-invitation |
| FR-019 | Thu hồi Lời mời | 3.2, 5.1 | InvitationsController + DELETE /organization/cancel-invitation |
| FR-020 | Xác minh Token Lời mời | 3.2, 5.1 | InvitationsController + GET /organization/verify-invitation |
| NFR-001 | Đăng nhập < 500ms | 2.1 | Redis cache, DB indexing |
| NFR-003 | bcrypt 12 vòng | 6 | bcrypt.hash(password, 12) |
| NFR-004 | Thời hạn JWT | 6 | Access: 15 phút, Refresh: 7 ngày |
| NFR-005 | Giới hạn tốc độ | 6 | Redis 10 req/min |