jwt token
JWT, Access Token, Refresh Token – Tất cả những gì mình học được khi chuẩn bị phỏng vấn Fullstack
Khi bắt đầu ôn phỏng vấn Fullstack, mình nghĩ JWT chỉ đơn giản là:
- Login
- Trả về JWT
- Frontend lưu token
- Gọi API
Sau khi tìm hiểu sâu hơn, mình nhận ra phía sau JWT có rất nhiều bài toán như Session, Logout, Role, Refresh Token, Rotation, OAuth, Forgot Password...
Bài viết này tổng hợp toàn bộ kiến thức mình đã hệ thống lại.
1. Session và JWT khác nhau như thế nào?
Đây là câu hỏi gần như chắc chắn sẽ xuất hiện khi phỏng vấn.
Session
Client
│
Session ID
│
Backend
│
Session Store (Redis/DB)
Backend lưu toàn bộ trạng thái đăng nhập.
Mỗi request đều phải tìm Session trong Redis hoặc Database.
Ưu điểm
- Logout rất dễ.
- Thu hồi quyền ngay lập tức.
- Dễ kiểm soát.
Nhược điểm
- Stateful.
- Khó scale hơn.
- Phải có Session Store dùng chung nếu có nhiều server.
JWT
Client
│
JWT
│
Backend
│
Verify Signature
Backend không lưu Access Token.
Chỉ cần kiểm tra:
- Chữ ký
- Thời gian hết hạn
Nếu hợp lệ thì cho phép truy cập.
Ưu điểm
- Stateless.
- Không cần lưu Session.
- Scale rất tốt.
Nhược điểm
- Logout khó.
- Không thu hồi Access Token ngay được.
2. JWT Stateless nghĩa là gì?
Đây là điều mình từng hiểu sai.
Stateless nghĩa là:
Backend không lưu trạng thái của Access Token.
Ví dụ
Frontend gửi
Authorization:
Bearer xxx
Backend chỉ làm
Verify Signature
↓
Token hết hạn chưa?
↓
OK
Không cần
SELECT ...
UPDATE ...
Redis ...
Đó mới là Stateless.
3. Access Token là gì?
Access Token chỉ có một nhiệm vụ.
Dùng để gọi API.
Ví dụ
GET /profile
Authorization:
Bearer xxx
Thường sống
- 5 phút
- 10 phút
- 15 phút
Không nên quá dài.
Nếu bị hacker lấy được thì thiệt hại chỉ trong khoảng thời gian ngắn.
4. Refresh Token là gì?
Refresh Token không dùng để gọi API.
Nó chỉ có nhiệm vụ:
Xin Access Token mới.
Ví dụ
Access Token
15 phút
Hết hạn.
Frontend gửi
POST /refresh
Refresh Token
Backend kiểm tra Refresh Token.
Nếu hợp lệ
↓
Sinh Access Token mới.
Refresh Token thường sống
- 7 ngày
- 30 ngày
- 90 ngày
5. Tại sao phải tách Access Token và Refresh Token?
Nếu chỉ có một JWT sống 30 ngày.
Hacker lấy được.
↓
Dùng được 30 ngày.
Nếu tách thành
Access Token
15 phút
+
Refresh Token
30 ngày
Thì nếu Access Token bị lộ.
↓
Hacker chỉ dùng được tối đa khoảng 15 phút.
Đó là lý do Access Token luôn có thời gian sống ngắn.
6. Nếu Role thay đổi thì sao?
Giả sử
JWT
role = Admin
5 phút sau.
Admin khác sửa Database
Role = User
Access Token cũ vẫn chứa
Admin
đến khi hết hạn.
Đây là nhược điểm của việc lưu Role trong JWT.
7. Có những cách nào để xử lý Role?
Cách 1
Lưu Role trong JWT.
Ưu điểm
- Không cần query Database.
Nhược điểm
- Role cập nhật chậm.
Cách 2
JWT chỉ lưu UserId.
Mỗi request
↓
Query Database
↓
Lấy Role.
Ưu điểm
- Role luôn đúng.
Nhược điểm
- Request nào cũng query Database.
Cách 3
JWT lưu
userId
tokenVersion
Database cũng lưu tokenVersion.
Nếu khác nhau.
↓
Reject.
Đây là cách nhiều hệ thống sử dụng để thu hồi token.
8. Nếu mỗi request đều query Database thì còn lợi ích gì của JWT?
Đây là câu hỏi rất hay.
Nếu request nào cũng
Verify JWT
↓
Query Database
thì lợi ích Stateless gần như mất đi.
Tuy nhiên JWT vẫn còn lợi ích:
- Không cần lưu Session.
- Dễ scale.
- Không cần Redis Session.
Đó là lý do nhiều hệ thống vẫn dùng JWT kết hợp Database.
9. Logout hoạt động như thế nào?
Đây là câu hỏi mình từng trả lời sai.
Khi Logout.
Client sẽ
- Xóa Access Token.
- Xóa Refresh Token.
Sau đó gọi API
POST /logout
Backend
↓
Revoke hoặc xóa Refresh Token.
Backend không cần xóa Access Token.
Vì backend không lưu Access Token.
10. Nếu hacker lấy được Access Token thì sao?
Giả sử
Access Token
15 phút
Người dùng Logout.
Refresh Token đã bị xóa.
Nhưng hacker vẫn giữ Access Token.
Backend không biết điều đó.
Vì JWT là Stateless.
Do đó hacker vẫn gọi API được cho đến khi Access Token hết hạn.
11. Muốn Logout ngay lập tức thì sao?
Có thể dùng
- Blacklist
- Token Version
- Redis
Flow
Verify JWT
↓
Check Redis
↓
Reject
Nhưng lúc này.
JWT không còn Stateless hoàn toàn nữa.
Đây là Trade-off giữa hiệu năng và bảo mật.
12. Blacklist và Whitelist
Blacklist
Danh sách Token bị cấm.
Request
↓
Verify JWT
↓
Check Blacklist
↓
Reject
Thường dùng cho Access Token.
Whitelist
Danh sách Token hợp lệ.
Request
↓
Có trong Database?
↓
OK
Không có
↓
401
Thường dùng cho Refresh Token.
13. Nếu hacker lấy được Refresh Token thì sao?
Refresh Token nguy hiểm hơn Access Token.
Ví dụ
Refresh Token sống 30 ngày.
Nếu hacker lấy được.
Họ có thể liên tục gọi
POST /refresh
để xin Access Token mới.
Cho đến khi Refresh Token hết hạn hoặc bị Revoke.
14. Refresh Token Rotation
Không Rotation
RT-1
↓
Refresh
↓
RT-1
↓
Refresh
↓
RT-1
Một Refresh Token dùng suốt 30 ngày.
Có Rotation
RT-1
↓
Refresh
↓
RT-2
↓
Refresh
↓
RT-3
Refresh Token cũ bị Revoke.
Client phải lưu Refresh Token mới.
Rotation không làm Refresh Token mất chức năng.
Nó chỉ giúp giảm rủi ro nếu Refresh Token bị lộ.
15. Rotation có nghĩa Refresh Token hết hạn sau 15 phút không?
Không.
Đây là điều mình từng nhầm.
Rotation chỉ có nghĩa:
Refresh Token thay đổi.
Không có nghĩa:
Refresh Token chỉ sống 15 phút.
Thời gian hết hạn của Refresh Token là quyết định của hệ thống.
Có hai cách.
Fixed Expiration
Refresh Token đổi liên tục.
Nhưng ngày hết hạn vẫn giữ nguyên.
Ví dụ
01/07
↓
31/07
Sliding Expiration
Mỗi lần Refresh.
↓
Gia hạn thêm 30 ngày.
16. Revoke là gì?
Revoke không nhất thiết là Delete.
Có thể
DELETE
hoặc
revoked = true
Mục tiêu đều giống nhau.
Token không còn sử dụng được.
17. Nhiều thiết bị đăng nhập
Một người dùng có thể đăng nhập
- Điện thoại
- Laptop
- Tablet
Database thường lưu
- user_id
- refresh_token_hash
- device
- ip
- expired_at
Nhờ đó có thể Logout từng thiết bị riêng.
18. Forgot Password
Flow
Email
↓
Reset Token
↓
Email
↓
Reset Password
↓
Delete/Revoke Token
Không bao giờ gửi Password qua Email.
Chỉ gửi Reset Token.
Reset Token phải:
- Có thời hạn.
- Chỉ dùng một lần.
Sau khi đổi Password.
Nên Revoke toàn bộ Refresh Token để buộc tất cả thiết bị đăng nhập lại.
19. Email Verification
Flow
Register
↓
Inactive
↓
Verify Token
↓
Click Email
↓
Active
Mục tiêu
- Xác minh Email tồn tại.
- Đảm bảo người đăng ký sở hữu Email đó.
Verify Token cũng nên:
- Có thời hạn.
- Chỉ dùng một lần.
20. Rate Limit
Ví dụ
Một IP hoặc một tài khoản.
Sai Password
5 lần/phút.
↓
429 Too Many Requests.
Hoặc
Khóa tạm thời 15 phút.
Rate Limit giúp chống Brute Force Attack.
21. OAuth Login (Google, GitHub...)
Flow
Login Google
↓
Authorization Code
↓
Backend
↓
Google Access Token
↓
Google Profile
↓
User Database
↓
JWT của hệ thống
Điều quan trọng cần nhớ là có hai loại Access Token:
- Access Token của Google.
- Access Token của hệ thống.
Sau khi lấy Profile từ Google.
Backend vẫn phải tạo JWT riêng để quản lý quyền và nghiệp vụ của ứng dụng.
22. JWT hay Session?
Không có đáp án đúng tuyệt đối.
Nếu hệ thống cần
- Scale tốt.
- Microservice.
- API.
JWT thường phù hợp.
Nếu hệ thống cần
- Thu hồi quyền ngay.
- Bảo mật rất cao.
- Quản lý Session tập trung.
Session hoặc JWT kết hợp Database/Redis sẽ phù hợp hơn.
Kết luận
Điều quan trọng nhất mình học được sau khi tìm hiểu JWT không phải là cách tạo Token.
Mà là hiểu rằng mọi giải pháp đều có Trade-off.
- Muốn Stateless thì chấp nhận Access Token không thể thu hồi ngay.
- Muốn thu hồi ngay thì phải lưu trạng thái ở Redis hoặc Database.
- Muốn người dùng ít phải đăng nhập lại thì cần Refresh Token.
- Muốn Refresh Token an toàn hơn thì dùng Rotation.
- Muốn bảo mật hơn nữa thì kết hợp HttpOnly Cookie, Rate Limit, MFA, Revoke và quản lý thiết bị.
Không có công nghệ nào hoàn hảo.
Điều quan trọng là chọn giải pháp phù hợp với yêu cầu của hệ thống.
All rights reserved