# 🔐 Hướng Dẫn Hệ Thống Đăng Nhập ## 📋 Mô Tả Quy Trình Đăng Nhập ### 1️⃣ Kiến Trúc Hệ Thống ``` ┌─────────────┐ ┌──────────────┐ ┌──────────────┐ │ Browser │────▶│ API Server │────▶│ Database │ │ (login.html)│◀────│ (Express) │◀────│ (MySQL) │ └─────────────┘ └──────────────┘ └──────────────┘ │ ▼ ┌──────────────┐ │ Redis │ │ (Cache) │ └──────────────┘ ``` ### 2️⃣ Quy Trình Đăng Nhập Chi Tiết #### **Bước 1: User Nhập Thông Tin** - Frontend (login.html) hiển thị form đăng nhập - User nhập `username` hoặc `email` và `password` - Click button "Đăng nhập" #### **Bước 2: Gửi Request Đến Server** ```javascript POST /api/users/login Content-Type: application/json { "username": "admin", // hoặc email "password": "admin123" } ``` #### **Bước 3: Server Xác Thực** **3.1. Tìm User trong Database** ```javascript // Tìm user theo username HOẶC email UsersAuth.findOne({ where: { OR: [ { username: username }, { email: username } ] } }) ``` **3.2. Kiểm Tra Các Điều Kiện** - ❌ User không tồn tại → Trả về lỗi 401 - ❌ Tài khoản bị khóa (`is_locked = true`) → Trả về lỗi 403 - ❌ Tài khoản không active (`is_active = false`) → Trả về lỗi 403 **3.3. Verify Password** ```javascript // Password được hash theo công thức: // hash = bcrypt(password + salt) const passwordMatch = await bcrypt.compare( password + user.salt, user.password_hash ); ``` **3.4. Xử Lý Kết Quả** **❌ Nếu Password Sai:** - Tăng `failed_login_attempts` lên 1 - Nếu thất bại ≥ 5 lần: - Khóa tài khoản (`is_locked = true`) - Set `locked_until` = hiện tại + 30 phút - Trả về lỗi 401 **✅ Nếu Password Đúng:** - Reset `failed_login_attempts = 0` - Tăng `login_count` lên 1 - Cập nhật `last_login` = thời gian hiện tại - Cập nhật `last_login_ip` = IP của client - Tạo `session_id` mới (UUID) #### **Bước 4: Tạo JWT Token** ```javascript const token = jwt.sign( { userId: user.id, username: user.username, email: user.email, sessionId: sessionId }, JWT_SECRET, { expiresIn: '24h' } ); ``` #### **Bước 5: Trả Response Về Client** ```json { "success": true, "message": "Đăng nhập thành công", "data": { "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "user": { "id": "uuid-here", "username": "admin", "email": "admin@senaai.tech", "profile": { ... }, "last_login": "2026-01-19T...", "login_count": 42 } } } ``` #### **Bước 6: Client Lưu Token** ```javascript // Lưu token vào localStorage localStorage.setItem('token', data.data.token); ``` ### 3️⃣ Sử Dụng Token Để Xác Thực **Các Request Tiếp Theo:** ```javascript fetch('/api/users/profile', { headers: { 'Authorization': 'Bearer ' + token } }) ``` **Server Verify Token:** ```javascript POST /api/users/verify-token Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... ``` ### 4️⃣ Đăng Xuất ```javascript POST /api/users/logout Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... // Server sẽ: // 1. Xóa current_session_id trong database // 2. Token sẽ không còn valid nữa ``` ## 🔒 Bảo Mật ### Password Security - **Salt**: Mỗi user có salt riêng (16 bytes random) - **Hash Algorithm**: bcrypt với cost factor = 10 - **Formula**: `hash = bcrypt(password + salt, 10)` ### Token Security - **Algorithm**: JWT với HS256 - **Expiration**: 24 giờ - **Secret Key**: Lưu trong environment variable - **Session Tracking**: Mỗi lần login tạo session_id mới ### Brute Force Protection - Khóa tài khoản sau 5 lần đăng nhập sai - Thời gian khóa: 30 phút - Tự động mở khóa sau khi hết thời gian ## 📊 Database Schema ### Table: `users_auth` ```sql - id (UUID, PK) - username (VARCHAR, UNIQUE) - email (VARCHAR, UNIQUE) - password_hash (VARCHAR) - salt (VARCHAR) - current_session_id (VARCHAR) - last_login (DATETIME) - last_login_ip (VARCHAR) - login_count (INT) - failed_login_attempts (INT) - is_active (BOOLEAN) - is_locked (BOOLEAN) - locked_until (DATETIME) ``` ## 🚀 API Endpoints ### Authentication | Method | Endpoint | Mô Tả | |--------|----------|-------| | POST | `/api/users/login` | Đăng nhập | | POST | `/api/users/register` | Đăng ký tài khoản mới | | POST | `/api/users/verify-token` | Xác thực token | | POST | `/api/users/logout` | Đăng xuất | ### User Management | Method | Endpoint | Mô Tả | |--------|----------|-------| | GET | `/api/users` | Lấy danh sách users | | GET | `/api/users/:id` | Lấy thông tin user theo ID | | POST | `/api/users` | Tạo user mới | | PUT | `/api/users/:id` | Cập nhật user | | DELETE | `/api/users/:id` | Xóa user (soft delete) | ## 🧪 Testing ### 1. Tạo Test Users ```bash node scripts/create-test-user.js ``` ### 2. Mở Login Page ``` http://localhost:4000/login.html ``` ### 3. Test Login **Test Accounts:** - **Admin**: `admin` / `admin123` - **Teacher**: `teacher1` / `teacher123` - **Student**: `student1` / `student123` ### 4. Test Với cURL **Login:** ```bash curl -X POST http://localhost:4000/api/users/login \ -H "Content-Type: application/json" \ -d '{"username":"admin","password":"admin123"}' ``` **Verify Token:** ```bash curl -X POST http://localhost:4000/api/users/verify-token \ -H "Authorization: Bearer YOUR_TOKEN_HERE" ``` **Logout:** ```bash curl -X POST http://localhost:4000/api/users/logout \ -H "Authorization: Bearer YOUR_TOKEN_HERE" ``` ## 🎯 Luồng Xử Lý (Flow Diagram) ``` User Input │ ▼ [Frontend] login.html │ POST /api/users/login │ { username, password } ▼ [Backend] userController.login() │ ├──▶ Tìm user trong DB │ ├─ Không tìm thấy → 401 Unauthorized │ └─ Tìm thấy ✓ │ ├──▶ Kiểm tra is_locked │ ├─ Bị khóa → 403 Forbidden │ └─ Không khóa ✓ │ ├──▶ Kiểm tra is_active │ ├─ Không active → 403 Forbidden │ └─ Active ✓ │ ├──▶ Verify password với bcrypt │ ├─ Sai → Tăng failed_attempts │ │ ├─ ≥5 lần → Khóa tài khoản │ │ └─ 401 Unauthorized │ └─ Đúng ✓ │ ├──▶ Cập nhật thông tin login │ ├─ Reset failed_attempts = 0 │ ├─ Tăng login_count │ ├─ Cập nhật last_login │ ├─ Cập nhật last_login_ip │ └─ Tạo session_id mới │ ├──▶ Tạo JWT token │ └─ Expire sau 24h │ └──▶ Return response └─ { token, user } │ ▼ [Frontend] Nhận response │ ├──▶ Lưu token vào localStorage ├──▶ Hiển thị thông báo thành công └──▶ Sử dụng token cho các request sau ``` ## 💡 Best Practices ### Frontend - ✅ Lưu token trong localStorage - ✅ Gửi token trong header: `Authorization: Bearer TOKEN` - ✅ Xử lý token expired (redirect về login) - ✅ Clear token khi logout ### Backend - ✅ Validate input (username, password) - ✅ Rate limiting để chống brute force - ✅ Log các lần đăng nhập thất bại - ✅ Use HTTPS trong production - ✅ Rotate JWT secret định kỳ ### Database - ✅ Index trên username, email - ✅ Không bao giờ lưu plain password - ✅ Use UUID cho ID - ✅ Soft delete (is_active) thay vì DELETE ## 🔍 Troubleshooting ### "401 Unauthorized" - Kiểm tra username/password có đúng không - Kiểm tra user có tồn tại trong DB không ### "403 Forbidden - Account Locked" - Tài khoản bị khóa do nhập sai password ≥5 lần - Đợi 30 phút hoặc admin mở khóa ### "Token Invalid" - Token đã expire (>24h) - Session đã bị logout - Đăng nhập lại để lấy token mới ## 📚 Dependencies - `bcrypt`: Hash passwords - `jsonwebtoken`: Tạo và verify JWT tokens - `crypto`: Generate salt và secrets - `sequelize`: ORM for MySQL - `express`: Web framework --- **Created by Sena Team** 🚀