Files
sena_db_api_layer/LOGIN_GUIDE.md
2026-01-19 09:33:35 +07:00

8.7 KiB
Raw Blame History

🔐 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 emailpassword
  • Click button "Đăng nhập"

Bước 2: Gửi Request Đến Server

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

// 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

// 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

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

{
  "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

// 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:

fetch('/api/users/profile', {
  headers: {
    'Authorization': 'Bearer ' + token
  }
})

Server Verify Token:

POST /api/users/verify-token
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

4 Đăng Xuất

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

- 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

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:

curl -X POST http://localhost:4000/api/users/login \
  -H "Content-Type: application/json" \
  -d '{"username":"admin","password":"admin123"}'

Verify Token:

curl -X POST http://localhost:4000/api/users/verify-token \
  -H "Authorization: Bearer YOUR_TOKEN_HERE"

Logout:

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 🚀