Quick Start: Chạy thử trong ‘một nốt nhạc’
Bắt tay vào làm luôn cho nóng. Đầu tiên, hãy dựng một project Node.js trắng tinh và cài các package ‘xương sống’ sau:
mkdir nodejs-jwt-auth && cd nodejs-jwt-auth
npm init -y
npm install express jsonwebtoken dotenv cookie-parser
Tạo file server.js và dán đoạn code login dưới đây. Bạn sẽ thấy ngay cơ chế hoạt động cơ bản nhất của JWT:
const express = require('express');
const jwt = require('jsonwebtoken');
require('dotenv').config();
const app = express();
app.use(express.json());
const ACCESS_TOKEN_SECRET = 'your_secret_key';
app.post('/login', (req, res) => {
const username = req.body.username;
const user = { name: username };
const accessToken = jwt.sign(user, ACCESS_TOKEN_SECRET, { expiresIn: '15m' });
res.json({ accessToken });
});
app.listen(3000, () => console.log('Server chạy tại port 3000'));
Đoạn code này chỉ mang tính chất ‘demo vui vẻ’. Thực tế, việc chỉ dùng accessToken với hạn dùng dài là một lỗ hổng chết người. Hacker chỉ cần lấy được token là có thể ‘làm mưa làm gió’ cho đến khi nó hết hạn.
Tại sao phải dùng Refresh Token Rotation? (Góc nhìn chuyên gia)
Vấn đề lớn nhất của Dev Backend là: Làm sao để user không phải login lại liên tục nhưng vẫn đảm bảo hacker không thể ‘nằm vùng’ quá lâu?
Giải pháp kinh điển là dùng bộ đôi: Access Token (sống ngắn, khoảng 15 phút) và Refresh Token (sống dài, ví dụ 7 ngày). Tuy nhiên, nếu Refresh Token bị lộ, hacker có thể dùng nó để tạo ra Access Token mới vô thời hạn.
Refresh Token Rotation giải quyết triệt để việc này bằng quy tắc ‘một lần rồi thôi’:
- Mỗi lần đổi Access Token mới, server cấp luôn một Refresh Token mới hoàn toàn.
- Token cũ bị khai tử ngay lập tức.
- Nếu server phát hiện ai đó cố dùng lại token cũ, hệ thống sẽ hiểu ngay là đang bị tấn công. Server sẽ ‘trảm’ toàn bộ các token liên quan, bắt buộc user phải login lại để xác thực danh tính.
Trong một dự án Fintech mình từng tham gia, việc áp dụng quy trình này giúp team tiết kiệm 20% thời gian xử lý các sự cố bảo mật liên quan đến session. Mọi thứ vào guồng và an tâm hơn hẳn.
Triển khai Refresh Token Rotation nâng cao
Đừng bao giờ phó mặc cho LocalStorage – ‘mồi ngon’ của tấn công XSS. Mình ưu tiên đẩy Refresh Token vào httpOnly Cookie để ‘cách ly’ hoàn toàn với JavaScript.
1. Cấu trúc Database
Bạn cần một nơi để lưu danh sách token đang hoạt động (whitelist). Ở ví dụ này mình dùng mảng cho đơn giản, nhưng với dự án thực tế có khoảng 10.000 user trở lên, Redis là lựa chọn số 1 vì tốc độ đọc/ghi cực nhanh.
let refreshTokens = []; // Trong thực tế hãy thay bằng Redis hoặc MongoDB
2. Logic xử lý ‘xoay vòng’ Token
Đây là trái tim của hệ thống. Chúng ta sẽ kiểm tra tính hợp lệ và xử lý tình huống Token Reuse (dùng lại token cũ).
app.post('/refresh', (req, res) => {
const refreshToken = req.cookies.refreshToken;
if (!refreshToken) return res.sendStatus(401);
// Phát hiện tấn công: Nếu token gửi lên không nằm trong whitelist
if (!refreshTokens.includes(refreshToken)) {
refreshTokens = []; // Thu hồi sạch sẽ token của user này
return res.sendStatus(403);
}
// Xóa ngay token vừa dùng
refreshTokens = refreshTokens.filter(t => t !== refreshToken);
jwt.verify(refreshToken, REFRESH_TOKEN_SECRET, (err, user) => {
if (err) return res.sendStatus(403);
// Cấp 'cặp đôi hoàn hảo' mới
const accessToken = jwt.sign({ name: user.name }, ACCESS_TOKEN_SECRET, { expiresIn: '15m' });
const newRefreshToken = jwt.sign({ name: user.name }, REFRESH_TOKEN_SECRET, { expiresIn: '7d' });
refreshTokens.push(newRefreshToken);
// Gửi về client qua cookie bảo mật
res.cookie('refreshToken', newRefreshToken, { httpOnly: true, secure: true, sameSite: 'Strict' });
res.json({ accessToken });
});
});
Kinh nghiệm ‘xương máu’ khi làm hệ thống Auth
Sau nhiều lần ‘vấp ngã’ ở các dự án lớn, mình rút ra 3 quy tắc bất biến:
- Tuyệt mật Payload: JWT chỉ được mã hóa Base64, không phải mã hóa dữ liệu. Đừng bao giờ nhét mật khẩu hay số căn cước vào đây. Chỉ nên để
userIdhoặcrole. - Cẩn trọng với Cookie: Luôn bật flag
httpOnly(ngăn JS đọc) vàsecure(chỉ gửi qua HTTPS). Điều này giúp bạn chặn đứng 90% các cuộc tấn công đánh cắp session thông thường. - Tận dụng Biến môi trường: Đừng bao giờ đẩy
SECRET_KEYlên GitHub. Hãy dùng.envvà quản lý chúng bằng AWS Secrets Manager hoặc HashiCorp Vault khi lên production.
Triển khai JWT không khó, nhưng làm cho nó ‘sạch’ và ‘an toàn’ mới là đẳng cấp của một Senior. Hãy áp dụng Refresh Token Rotation ngay hôm nay để bảo vệ người dùng của bạn tốt hơn. Chúc các bạn có những dòng code sạch và bảo mật!

