Opaque Token là một chuỗi không thể giải mã hay lấy thông tin gì từ phía client – nó chỉ đóng vai trò như một "mã tham chiếu" tới phiên đăng nhập được lưu ở phía server.

1. JWT (JSON Web Token) là gì?
Trước khi phân tích Opaque Token, hãy nhắc lại khái niệm cơ bản về JWT.
JWT là loại token có thể tự mang theo thông tin người dùng. Nó có 3 phần: Header, Payload và Signature – được nối bằng dấu .
Ví dụ: xxxxx.yyyyy.zzzzz
-
Payload: Lưu các claims như userId
, roles
, email
… Tuy nhiên, phần này chỉ được mã hóa base64 nên dễ bị đọc bởi bất kỳ ai.
Đặc điểm nổi bật của JWT là stateless: Server không cần lưu trạng thái token – mọi thông tin đã nằm trong payload. Khi client gửi lại token, server chỉ cần xác thực chữ ký là đủ.
2. Opaque Token là gì?
Khác với JWT, Opaque Token (hay còn gọi là Reference Token) chỉ là một chuỗi ngẫu nhiên không mang thông tin gì bên trong.
Ví dụ: v2.local.adsjSDA723b1-asdnsa...
Token này không tự nói lên điều gì – nó chỉ được dùng như khóa để truy xuất thông tin phiên đăng nhập (session) trên server.
Cách hoạt động của Opaque Token:
3. So sánh Opaque Token và JWT
Việc lựa chọn giữa hai loại token tùy thuộc vào yêu cầu hệ thống. Bảng so sánh dưới đây giúp bạn hiểu rõ sự khác biệt:
Tiêu chí
|
Opaque Token
|
JWT (JSON Web Token)
|
Cấu trúc |
Chuỗi ngẫu nhiên, không chứa thông tin gì. |
Có 3 phần: header, payload, signature – có thể giải mã payload. |
Trạng thái
|
Có trạng thái (stateful): server cần lưu session. |
Không trạng thái (stateless): không cần lưu gì trên server. |
Bảo mật
|
Cao hơn: nếu token bị lộ cũng không có thông tin. Dễ thu hồi. |
Thấp hơn: payload có thể bị đọc. Thu hồi khó nếu chưa hết hạn. |
Hiệu năng
|
Chậm hơn: cần truy vấn DB để xác thực. |
Nhanh hơn: chỉ cần xác thực chữ ký, không cần I/O. |
Kích thước
|
Gọn nhẹ, độ dài token cố định. |
Thường dài hơn do chứa thông tin bên trong. |
Mở rộng trong hệ thống
|
Phức tạp hơn với kiến trúc microservices – cần gọi tới service xác thực trung tâm. |
Dễ mở rộng hơn – các service có thể tự xác thực token bằng secret. |
Khi nào nên chọn Opaque Token?
Khi nào nên chọn JWT?
4. Hướng dẫn triển khai Opaque Token với Node.js, Express và TypeScript
Trong phần này, ta sẽ xây dựng hệ thống xác thực sử dụng Opaque Token. Redis sẽ được dùng làm nơi lưu session vì có tốc độ xử lý rất nhanh.
Yêu cầu:
4.1 Khởi tạo dự án và cài đặt thư viện
Cấu trúc thư mục:
opaque-token-example/
├── src/
│ ├── authMiddleware.ts
│ └── server.ts
├── package.json
└── tsconfig.json
mkdir opaque-token-example
cd opaque-token-example
npm init -y
npm install express redis uuid
npm install -D typescript @types/express @types/node @types/uuid ts-node-dev
Tạo file tsconfig.json
:
4.2 Viết mã nguồn
src/server.ts
: File chính, định nghĩa các endpoint như login, logout, và truy cập tài nguyên.
import express, { Request, Response, NextFunction } from 'express';
import { createClient } from 'redis';
import { v4 as uuidv4 } from 'uuid';
import { authMiddleware } from './authMiddleware';
const app = express();
app.use(express.json());
// Khởi tạo Redis client
export const redisClient = createClient({
// url: 'redis://your-redis-instance' // Cấu hình nếu cần
});
redisClient.on('error', (err) => console.log('Redis Client Error', err));
// Dữ liệu người dùng giả lập
const users = [{
id: 'user-1',
username: 'testdev',
password: 'password123'
}];
// Endpoint: Đăng nhập
app.post('/login', async (req, res) => {
const { username, password } = req.body;
const user = users.find(u => u.username === username && u.password === password);
if (!user) {
return res.status(401).json({ message: 'Invalid credentials' });
}
// 1. Tạo Opaque Token ngẫu nhiên
const token = uuidv4();
// 2. Lưu thông tin session vào Redis với token làm key
// Session sẽ hết hạn sau 1 giờ (3600 giây)
await redisClient.set(token, JSON.stringify({ userId: user.id }), {
EX: 3600
});
// 3. Trả token về cho client
return res.json({ accessToken: token });
});
// Endpoint: Lấy thông tin cá nhân (được bảo vệ)
// Chúng ta sẽ sử dụng middleware xác thực ở đây
app.get('/profile', authMiddleware, (req: Request, res: Response) => {
// Nhờ có middleware, chúng ta có thể truy cập thông tin user qua req.user
res.json({ message: 'Đây là thông tin profile của bạn', user: req.user });
});
// Endpoint: Đăng xuất
app.post('/logout', authMiddleware, async (req, res) => {
// Để đăng xuất, chỉ cần xóa token khỏi Redis
const token = req.headers.authorization?.split(' ')[1];
if (token) {
await redisClient.del(token);
}
res.status(200).json({ message: 'Logged out successfully' });
});
const startServer = async () => {
await redisClient.connect();
app.listen(3000, () => {
console.log('Server is running on http://localhost:3000');
});
};
startServer();
src/authMiddleware.ts
: Middleware xác thực các request dựa vào token gửi lên.
import { Request, Response, NextFunction } from 'express';
import { redisClient } from './server';
// Mở rộng kiểu Request của Express để chứa thông tin user
declare global {
namespace Express {
interface Request {
user?: any;
}
}
}
export const authMiddleware = async (req: Request, res: Response, next: NextFunction) => {
const authHeader = req.headers.authorization;
if (!authHeader || !authHeader.startsWith('Bearer ')) {
return res.status(401).json({ message: 'Authorization token required' });
}
const token = authHeader.split(' ')[1];
try {
// Tra cứu token trong Redis
const sessionData = await redisClient.get(token);
if (!sessionData) {
// Nếu không tìm thấy -> token không hợp lệ hoặc đã hết hạn
return res.status(401).json({ message: 'Invalid or expired token' });
}
// Gắn thông tin user vào request để các handler sau có thể sử dụng
req.user = JSON.parse(sessionData);
next();
} catch (error) {
console.error('Authentication error:', error);
return res.status(500).json({ message: 'Internal server error' });
}
};
4.3 Chạy ứng dụng
Thêm script vào package.json
:
"scripts": {
"start": "ts-node-dev --respawn --transpile-only src/server.ts"
}
Chạy ứng dụng bằng lệnh:
5. Kết luận
Không có loại token nào tốt hơn tuyệt đối – chỉ có giải pháp phù hợp với từng bài toán.
Việc hiểu rõ cách hoạt động của cả hai sẽ giúp bạn xây dựng hệ thống xác thực đúng đắn và linh hoạt hơn. Mong rằng bài viết này đã giúp bạn nắm vững khái niệm và cách ứng dụng Opaque Token trong thực tế với Node.js + TypeScript.