Chương 36. Authentication

Từ chương này, ta bước sang phần bảo mật và phân quyền.

Chủ đề đầu tiên là:

Authentication.

Nói đơn giản:

> Authentication là xác nhận người dùng là ai.

Ví dụ:

Đây có thật là tài khoản của bạn không?
Bạn có chứng minh được bạn là user_123 không?

Authentication thường bắt đầu bằng đăng nhập.

Nhưng đăng nhập không chỉ là một form email/password.

Nó liên quan đến:

  • Session.
  • Cookie.
  • Token.
  • JWT.
  • OAuth2.
  • OpenID Connect.
  • Access token.
  • Refresh token.
  • Password reset.
  • MFA.
  • Device/session management.
  • Security trade-off.

Thông điệp chính của chương:

> Authentication trả lời câu hỏi "bạn là ai?". Authorization trả lời câu hỏi "bạn được làm gì?". Đừng trộn hai chuyện này trong đầu.

---

36.1. Ví dụ dễ hiểu: vào tòa nhà

Bạn đến một tòa nhà văn phòng.

Bảo vệ hỏi:

Bạn là ai?

Bạn đưa thẻ nhân viên.

Bảo vệ kiểm tra thẻ và xác nhận:

Đúng, bạn là nhân viên công ty.

Đó là authentication.

Nhưng sau đó bạn có được vào phòng server không?

Bạn có được vào phòng kế toán không?

Bạn có được mở két không?

Đó là authorization.

Hai chuyện khác nhau:

Authentication:
  Bạn là ai?

Authorization:
  Bạn được làm gì?

Trong web app cũng vậy.

---

36.2. Đăng nhập thật ra làm gì?

Khi user đăng nhập bằng email/password:

1. User gửi email/password.
2. Backend tìm user.
3. Backend kiểm tra password có đúng không.
4. Nếu đúng, backend tạo một cách để nhớ user đã đăng nhập.
5. Những request sau không cần gửi password lại.

Điểm quan trọng:

Password chỉ nên dùng để chứng minh lúc đăng nhập.

Sau khi đăng nhập thành công, hệ thống thường dùng:

  • Session id trong cookie.
  • Hoặc token.
  • Hoặc kết hợp nhiều loại token.

Không nên gửi email/password ở mọi request.

---

36.3. Vì sao cần "nhớ" user đã đăng nhập?

HTTP về bản chất là request/response.

Mỗi request độc lập.

Ví dụ:

GET /assignments
GET /submissions/123
POST /submissions

Nếu không có cơ chế nhớ đăng nhập, mỗi request backend đều không biết:

Ai đang gọi đây?

Vì vậy cần một bằng chứng đi kèm request.

Ví dụ:

Cookie chứa session id.
Authorization header chứa access token.

Backend dùng bằng chứng đó để xác định user hiện tại.

---

36.4. Session là gì?

Session là trạng thái đăng nhập được lưu ở server.

Ví dụ:

session_id = abc123
user_id = user_9
expires_at = ...

Server lưu session trong:

  • Database.
  • Redis.
  • Memory nếu app đơn giản.
  • Session store chuyên dụng.

Browser giữ một mã session id.

Khi request lên backend, browser gửi session id.

Backend tra session id để biết:

Đây là user nào?
Session còn hạn không?
Session có bị revoke không?

Nói dễ hiểu:

> Session giống một phiếu gửi đồ. User cầm mã phiếu, server giữ sổ ghi mã phiếu này thuộc về ai.

---

36.5. Cookie là gì?

Cookie là dữ liệu nhỏ browser tự lưu và tự gửi lại cho website phù hợp.

Ví dụ server trả:

Set-Cookie: session_id=abc123

Sau đó browser tự gửi:

Cookie: session_id=abc123

trong các request đến domain đó.

Cookie thường dùng với session-based auth.

Backend không cần frontend tự gắn token vào header.

Browser lo gửi cookie.

Nhưng cookie có vấn đề bảo mật riêng:

  • CSRF.
  • XSS nếu cookie không cấu hình tốt.
  • SameSite.
  • Secure.
  • HttpOnly.

Cookie không xấu.

Cookie là cơ chế rất phổ biến và vẫn rất hợp cho web app truyền thống.

---

36.6. HttpOnly, Secure, SameSite

Khi dùng cookie auth, ba cấu hình rất quan trọng:

HttpOnly
Secure
SameSite

HttpOnly:

JavaScript trong browser không đọc được cookie.

Điều này giảm rủi ro nếu có XSS.

Secure:

Cookie chỉ gửi qua HTTPS.

SameSite:

Giảm rủi ro request từ site khác tự mang cookie sang.

Ví dụ cookie session tốt thường có dạng tư duy:

Set-Cookie: session_id=...; HttpOnly; Secure; SameSite=Lax

Không cần nhớ cú pháp ngay.

Chỉ cần nhớ:

> Cookie auth phải được cấu hình cẩn thận, không phải cứ set cookie là xong.

---

36.7. Session-based auth hoạt động như thế nào?

Luồng:

1. User login bằng email/password.
2. Backend kiểm tra đúng.
3. Backend tạo session trong Redis/database.
4. Backend set cookie session_id cho browser.
5. Browser tự gửi cookie ở request sau.
6. Backend tra session_id để lấy current_user.

Ví dụ:

Browser -> POST /login
Backend -> Set-Cookie: session_id=abc123

Browser -> GET /me
Cookie: session_id=abc123
Backend -> user_9

Ưu điểm:

  • Dễ revoke session.
  • Dễ quản lý logout.
  • Dễ thấy session nào đang active.
  • Phù hợp web app server-rendered hoặc SPA cùng domain.
  • Không lộ thông tin user trong token client cầm.

Nhược điểm:

  • Server cần session store.
  • Scale nhiều server cần shared session store hoặc sticky session.
  • Cross-domain/mobile/API cần thiết kế kỹ hơn.

---

36.8. JWT là gì?

JWT là JSON Web Token.

Nó là một token có cấu trúc và có chữ ký.

Một JWT thường có ba phần:

header.payload.signature

Payload có thể chứa claims.

Ví dụ:

{
  "sub": "user_9",
  "exp": 1770000000,
  "iss": "synvia-auth",
  "aud": "synvia-api"
}

Backend có thể kiểm tra chữ ký để biết token có bị sửa không.

Nói dễ hiểu:

> JWT giống một giấy xác nhận có chữ ký. Server đọc giấy và kiểm tra chữ ký để biết giấy có hợp lệ không.

---

36.9. JWT không phải mã hóa

Hiểu nhầm rất thường gặp:

JWT đã được bảo mật nên nhét gì vào cũng được.

Sai.

JWT thường chỉ được ký, không được mã hóa.

Payload có thể đọc được nếu ai đó cầm token.

Vì vậy không nên nhét vào JWT:

  • Password.
  • Token bí mật khác.
  • Dữ liệu cá nhân nhạy cảm.
  • Permission quá chi tiết nếu không cần.
  • Nội dung private.

JWT nên chứa thông tin tối thiểu để xác thực/ủy quyền cơ bản.

Ví dụ:

sub
issuer
audience
expiration
scope/role nếu phù hợp

Chữ ký giúp phát hiện token bị sửa.

Nó không làm payload biến thành bí mật.

---

36.10. Stateless auth là gì?

JWT thường được dùng cho stateless auth.

Stateless nghĩa là:

Backend không cần tra session store ở mỗi request.
Backend chỉ verify token.

Luồng:

1. User login.
2. Backend trả access token.
3. Client gửi token trong Authorization header.
4. Backend verify chữ ký và exp.
5. Nếu hợp lệ, backend biết user là ai.

Ví dụ:

Authorization: Bearer eyJ...

Ưu điểm:

  • API dễ scale ngang.
  • Service khác có thể verify token nếu có public key.
  • Hợp với mobile app/API/microservices.

Nhược điểm:

  • Revoke token khó hơn session.
  • Token bị lộ thì dùng được tới khi hết hạn.
  • Payload có thể lỗi thời.
  • Dễ lạm dụng và nhét quá nhiều thứ vào token.

Stateless nghe gọn.

Nhưng không miễn phí.

---

36.11. Session vs JWT

Không có cái nào luôn tốt hơn.

Session phù hợp khi:

  • Web app cùng domain.
  • Muốn logout/revoke dễ.
  • Muốn quản lý active sessions.
  • Muốn server kiểm soát chặt.
  • Team không muốn tự xử lý token phức tạp.

JWT phù hợp khi:

  • API được gọi từ nhiều client.
  • Mobile app.
  • Microservices cần verify token.
  • Dùng identity provider bên ngoài.
  • Access token ngắn hạn.
  • Có refresh token flow rõ.

Bảng ngắn:

| Tiêu chí | Session | JWT | |---|---|---| | Server lưu trạng thái | Có | Thường không | | Revoke dễ | Dễ hơn | Khó hơn nếu không có blacklist/introspection | | Scale API | Cần session store | Dễ verify phân tán hơn | | Lộ token/session | Đều nguy hiểm | Đều nguy hiểm | | Web cùng domain | Rất hợp | Có thể dùng nhưng không luôn cần | | Mobile/API | Dùng được nhưng kém tiện hơn | Rất phổ biến |

Điểm quan trọng:

> Chọn session hay JWT dựa trên client, domain, revoke requirement, team skill, và hạ tầng, không dựa trên cảm giác cái nào "hiện đại" hơn.

---

36.12. Access token là gì?

Access token là token dùng để gọi API.

Nó nói:

Người cầm token này được truy cập API trong phạm vi và thời gian nhất định.

Access token nên ngắn hạn.

Ví dụ:

5 phút
15 phút
1 giờ

Tùy hệ thống.

Vì nếu access token bị lộ, thời gian bị lợi dụng sẽ ngắn hơn.

Access token thường được gửi trong:

Authorization: Bearer <token>

Không nên để access token sống quá lâu nếu không có lý do rõ.

---

36.13. Refresh token là gì?

Refresh token dùng để lấy access token mới.

Luồng:

1. User login.
2. Server cấp access token ngắn hạn và refresh token dài hạn hơn.
3. Client dùng access token gọi API.
4. Khi access token hết hạn, client dùng refresh token xin access token mới.

Refresh token thường sống lâu hơn access token.

Vì vậy refresh token phải được bảo vệ kỹ hơn.

Có thể lưu refresh token trong:

  • Secure HttpOnly cookie.
  • Secure storage trên mobile.
  • Server-side session store.

Không nên vứt refresh token lung tung trong localStorage nếu không hiểu rủi ro XSS.

Refresh token cũng nên có:

  • Expiration.
  • Rotation.
  • Revoke.
  • Device/session tracking.

---

36.14. Refresh token rotation

Refresh token rotation nghĩa là:

Mỗi lần dùng refresh token, server cấp refresh token mới và vô hiệu hóa token cũ.

Lợi ích:

Nếu refresh token cũ bị đánh cắp và attacker dùng lại, hệ thống có thể phát hiện reuse.

Ví dụ:

Client dùng refresh_token_A.
Server trả access_token mới + refresh_token_B.
Server đánh dấu A đã dùng.

Sau đó ai đó dùng lại A.
Server biết có vấn đề.

Rotation làm hệ thống an toàn hơn.

Nhưng cũng phức tạp hơn:

  • Cần lưu state refresh token.
  • Cần xử lý race condition.
  • Cần revoke session khi phát hiện reuse.

Đây là ví dụ cho thấy JWT không hoàn toàn stateless nếu muốn bảo mật tốt ở mức refresh token.

---

36.15. Lưu token ở đâu?

Đây là câu hỏi hay gây tranh luận.

Các lựa chọn thường gặp:

HttpOnly cookie
memory
localStorage
secure mobile storage

HttpOnly cookie:

  • JavaScript không đọc được.
  • Tốt để giảm rủi ro XSS lấy token.
  • Cần xử lý CSRF/SameSite.

Memory:

  • Mất khi reload tab.
  • Ít bền hơn.
  • Có thể kết hợp refresh token cookie.

localStorage:

  • Dễ dùng.
  • JavaScript đọc được.
  • Nếu có XSS, token dễ bị lấy.

Mobile secure storage:

  • Hợp với mobile app.
  • Vẫn cần thiết kế expiration/revoke.

Không có nơi lưu token tuyệt đối an toàn.

Câu hỏi đúng là:

> Threat model của app là gì, client là gì, token sống bao lâu, và nếu bị lộ thì revoke thế nào?

---

36.16. XSS và CSRF liên quan auth thế nào?

XSS là khi attacker chạy được JavaScript trong website của bạn.

Nếu token nằm trong localStorage, XSS có thể đọc token.

Nếu cookie không HttpOnly, XSS có thể đọc cookie.

HttpOnly cookie giúp giảm rủi ro này.

CSRF là khi một site khác lừa browser gửi request đến site của bạn kèm cookie.

Vì browser tự gửi cookie, CSRF là rủi ro với cookie auth.

Cách giảm:

  • SameSite cookie.
  • CSRF token cho request nhạy cảm.
  • Kiểm tra Origin/Referer.
  • Không dùng GET cho hành động thay đổi dữ liệu.

Điểm thực tế:

Cookie giúp chống lộ token qua JavaScript tốt hơn nếu HttpOnly.
Nhưng cần nghĩ CSRF.

Bearer token trong header ít bị CSRF hơn.
Nhưng nếu lưu trong localStorage, cần nghĩ XSS.

Bảo mật là trade-off.

---

36.17. OAuth2 là gì ở mức dễ hiểu?

OAuth2 là cơ chế ủy quyền truy cập.

Nó trả lời câu hỏi:

App A có được phép truy cập tài nguyên của user ở Service B không?

Ví dụ:

Bạn dùng một app lịch.
App đó muốn đọc Google Calendar của bạn.
Google hỏi: bạn có cho phép app này đọc calendar không?
Nếu bạn đồng ý, Google cấp token cho app.

OAuth2 ban đầu không phải để "đăng nhập".

Nó là để:

Ủy quyền cho một ứng dụng truy cập tài nguyên.

Nhưng nhiều người dùng OAuth2 để login thông qua nhà cung cấp như Google.

Để login đúng nghĩa, cần thêm OpenID Connect.

---

36.18. OpenID Connect là gì?

OpenID Connect, viết tắt OIDC, là lớp đăng nhập xây trên OAuth2.

Nó giúp app biết:

User này là ai?

OIDC thường trả về id_token.

id_token chứa thông tin identity của user.

Ví dụ:

sub
email
name
issuer
expiration

Nói dễ hiểu:

OAuth2:
  App được phép truy cập cái gì?

OpenID Connect:
  User đăng nhập này là ai?

Khi bạn thấy nút:

Login with Google

thường đó là OAuth2 + OIDC.

---

36.19. Login with Google diễn ra như thế nào?

Luồng đơn giản:

1. User bấm Login with Google.
2. App redirect user sang Google.
3. User đăng nhập Google và đồng ý.
4. Google redirect user về app với code.
5. Backend app đổi code lấy token.
6. Backend verify id_token.
7. Backend biết Google xác nhận user là ai.
8. App tạo user/session nội bộ.

Điểm quan trọng:

App không nhận password Google của user.

App chỉ nhận kết quả xác thực từ Google.

Sau đó app vẫn nên có user nội bộ.

Ví dụ:

google_sub -> internal_user_id

Không nên để toàn bộ logic domain phụ thuộc trực tiếp vào email Google một cách lỏng lẻo.

---

36.20. OAuth2 authorization code flow

Flow phổ biến và nên dùng cho web app là authorization code flow.

Ý tưởng:

Frontend không nhận token nhạy cảm trực tiếp từ URL.
Backend đổi code lấy token.

Luồng:

Browser -> Identity Provider
Identity Provider -> Browser redirect về app với code
Browser -> Backend gửi code
Backend -> Identity Provider đổi code lấy token
Backend -> tạo session/token nội bộ

Với SPA/mobile, thường dùng thêm PKCE.

Không cần thuộc lòng chi tiết ngay.

Chỉ cần nhớ:

> Với OAuth/OIDC, đừng tự chế flow. Dùng thư viện/provider chuẩn và flow hiện đại như Authorization Code + PKCE khi phù hợp.

---

36.21. Identity Provider là gì?

Identity Provider là dịch vụ chuyên xác thực identity.

Ví dụ:

  • Google.
  • Microsoft Entra ID.
  • Okta.
  • Auth0.
  • Cognito.
  • Clerk.
  • Firebase Auth.
  • Keycloak nếu tự host.
  • Supabase Auth.

Nó xử lý:

  • Login.
  • Password.
  • Social login.
  • MFA.
  • Password reset.
  • Email verification.
  • Token.
  • OIDC/OAuth.
  • User federation.

App của bạn có thể tin identity provider để xác nhận user.

Nhưng app vẫn phải xử lý authorization theo domain.

Ví dụ:

Google nói: đây là baonguyen@gmail.com.

App phải tự quyết:
  user này thuộc lớp nào?
  có được xem assignment nào?
  có phải teacher không?

---

36.22. Khi nào nên dùng dịch vụ auth có sẵn?

Nên cân nhắc dùng dịch vụ auth có sẵn khi:

  • Team nhỏ.
  • Không muốn tự xử lý password security.
  • Cần social login.
  • Cần enterprise SSO.
  • Cần MFA.
  • Cần password reset/email verification.
  • Cần audit/security tốt.
  • Auth không phải năng lực lõi của sản phẩm.

Dùng provider giúp tránh nhiều lỗi nguy hiểm.

Nhưng cũng có trade-off:

  • Chi phí.
  • Vendor lock-in.
  • Tùy biến flow khó hơn.
  • Phụ thuộc uptime provider.
  • Migration user sau này cần kế hoạch.

Thực dụng mà nói:

> Nếu auth không phải thứ bạn muốn trở thành chuyên gia vận hành, dùng provider tốt thường là lựa chọn khôn ngoan.

---

36.23. Khi nào tự làm auth?

Tự làm auth có thể hợp khi:

  • Yêu cầu rất đơn giản.
  • Hệ thống nội bộ.
  • Có đội đủ kinh nghiệm security.
  • Cần kiểm soát đặc biệt.
  • Không muốn phụ thuộc provider.
  • Có yêu cầu pháp lý/hạ tầng riêng.

Nhưng tự làm auth nghĩa là bạn phải tự chịu:

  • Hash password đúng.
  • Password reset an toàn.
  • Email verification.
  • Session management.
  • Token rotation.
  • Brute force protection.
  • Account lock/rate limit.
  • MFA nếu cần.
  • Audit log.
  • Security patch.

Auth nhìn nhỏ.

Nhưng lỗi auth thường rất đắt.

---

36.24. Password nên được lưu thế nào?

Không bao giờ lưu password plain text.

Không nên tự nghĩ thuật toán hash.

Password nên được hash bằng thuật toán chuyên cho password.

Ví dụ:

  • Argon2.
  • bcrypt.
  • scrypt.

Không nên dùng hash nhanh kiểu:

MD5
SHA1
SHA256 đơn thuần

Vì password cần hash chậm để chống brute force.

Hệ thống lưu:

password_hash

Không lưu:

password

Khi user login:

User gửi password.
Backend dùng thuật toán verify với password_hash.
Nếu khớp thì login thành công.

---

36.25. Password reset nguy hiểm hơn nhiều người nghĩ

Password reset là đường vào tài khoản.

Nếu làm sai, attacker có thể chiếm account.

Một luồng an toàn hơn:

1. User nhập email.
2. Backend luôn trả thông báo chung, không tiết lộ email có tồn tại hay không.
3. Nếu email tồn tại, tạo reset token ngẫu nhiên, ngắn hạn.
4. Gửi link reset qua email.
5. User mở link, nhập password mới.
6. Backend verify token, đổi password.
7. Revoke session cũ nếu cần.

Reset token nên:

  • Ngẫu nhiên mạnh.
  • Hết hạn nhanh.
  • Dùng một lần.
  • Lưu dạng hash nếu cẩn thận.
  • Không log ra ngoài.

Không nên:

Gửi password mới qua email.

---

36.26. Brute force protection

Brute force là thử nhiều password.

Cần chống bằng:

  • Rate limit login.
  • Delay tăng dần.
  • Lock tạm thời nếu quá nhiều lần sai.
  • CAPTCHA trong một số trường hợp.
  • Alert đăng nhập lạ.
  • MFA.
  • Kiểm tra password đã bị lộ nếu có thể.

Nhưng cũng phải tránh làm hại user thật.

Ví dụ:

Attacker cố tình nhập sai password của user nhiều lần để khóa tài khoản user.

Vì vậy lockout cần thiết kế cẩn thận.

Rate limit theo IP, account, device, và pattern thường tốt hơn một rule đơn giản.

---

36.27. MFA là gì?

MFA là multi-factor authentication.

Nó yêu cầu nhiều hơn một yếu tố xác thực.

Ví dụ:

Password + mã trong app authenticator.
Password + security key.
Password + email verification step.

Các yếu tố thường là:

  • Thứ bạn biết: password.
  • Thứ bạn có: điện thoại/security key.
  • Thứ bạn là: biometric.

MFA giúp giảm rủi ro khi password bị lộ.

Với admin, teacher, tài khoản có quyền cao, MFA rất đáng cân nhắc.

SMS OTP tốt hơn không có gì, nhưng không phải lựa chọn mạnh nhất.

Authenticator app hoặc security key thường tốt hơn.

---

36.28. Logout thật ra làm gì?

Logout nghĩa là làm cho bằng chứng đăng nhập không dùng được nữa.

Với session:

Xóa session ở server.
Xóa cookie ở browser.

Với JWT access token stateless:

Access token có thể vẫn hợp lệ tới khi hết hạn.

Vì vậy access token nên ngắn hạn.

Refresh token/session nên bị revoke khi logout.

Nếu cần logout ngay lập tức mọi nơi, cần:

  • Server-side session.
  • Token blacklist.
  • Token version.
  • Introspection.
  • Hoặc thiết kế provider hỗ trợ revoke.

Logout đơn giản trên UI không đủ.

Nó phải làm mất hiệu lực credential phía backend.

---

36.29. Device/session management

Một tính năng rất hữu ích:

Xem các thiết bị đang đăng nhập.
Đăng xuất thiết bị lạ.
Đăng xuất tất cả.

Muốn làm được, hệ thống cần lưu session/device.

Ví dụ:

session_id
user_id
device_name
ip
user_agent
created_at
last_seen_at
revoked_at

Session-based auth làm việc này khá tự nhiên.

JWT thuần stateless khó hơn nếu không có server-side tracking.

Đây là một lý do nhiều hệ thống thực tế không "stateless tuyệt đối".

Họ vẫn lưu thông tin session/refresh token để quản lý bảo mật.

---

36.30. AI Judge cần authentication thế nào?

Trong AI Judge, có nhiều loại user:

  • Student.
  • Teacher.
  • Admin.
  • School owner.
  • Internal operator.

Authentication trả lời:

Request này đến từ user nào?

Ví dụ:

user_id = user_9
auth_method = password
session_id = ...

Sau khi biết user là ai, chương sau mới quyết định:

User này có được xem assignment này không?
User này có phải teacher của course này không?
User này có được override điểm không?

Authentication không nên tự ôm hết logic phân quyền.

Nó chỉ xác nhận identity.

---

36.31. AI Judge: chọn session hay JWT?

Nếu AI Judge là web app cùng domain:

Frontend và backend cùng domain hoặc subdomain được kiểm soát tốt.

Session cookie HttpOnly có thể rất hợp.

Ưu điểm:

  • Dễ logout.
  • Dễ revoke.
  • Dễ quản lý session teacher/admin.
  • Giảm rủi ro token bị JavaScript đọc.

Nếu AI Judge có mobile app hoặc nhiều client API:

Access token + refresh token

có thể phù hợp hơn.

Nếu dùng Google/Microsoft login cho trường học:

OIDC với identity provider

rất đáng cân nhắc.

Một thiết kế thực dụng:

Web app:
  HttpOnly session cookie.

Mobile/API:
  OAuth/OIDC style token flow.

Enterprise school:
  SSO qua Google/Microsoft/Okta nếu cần.

Không cần ép mọi client dùng một kiểu nếu nhu cầu khác nhau.

---

36.32. AI Judge: teacher/admin nên bảo vệ kỹ hơn

Tài khoản teacher/admin có quyền cao hơn student.

Nếu bị chiếm, hậu quả lớn hơn:

  • Xem dữ liệu nhiều học sinh.
  • Sửa điểm.
  • Xem feedback.
  • Xuất báo cáo.
  • Quản lý lớp.
  • Gọi nhiều job tốn chi phí.

Vì vậy nên cân nhắc:

  • MFA cho teacher/admin.
  • Alert đăng nhập lạ.
  • Session timeout hợp lý.
  • Audit log hành động nhạy cảm.
  • Re-auth trước hành động rất nhạy cảm.

Ví dụ:

Teacher đổi rubric chấm điểm hàng loạt.

Có thể yêu cầu xác nhận lại.

Authentication không chỉ là login một lần rồi quên.

Nó là cả vòng đời phiên đăng nhập.

---

36.33. API key có phải authentication không?

API key cũng là một dạng xác thực.

Nó thường dùng cho:

  • Service gọi service.
  • Developer API.
  • Integration.
  • Webhook.

API key trả lời:

Request này đến từ app/service/key nào?

Nhưng API key thường không đại diện cho human user.

Ví dụ:

school_integration_key_123

khác với:

user_9

API key sẽ được nói kỹ hơn ở chương service-to-service security.

Ở đây chỉ cần nhớ:

> Không phải mọi caller đều là người dùng. Có caller là service, job, integration, script.

---

36.34. Anonymous user

Không phải request nào cũng có user đăng nhập.

Ví dụ:

  • Xem trang public.
  • Mở landing page.
  • Xem course preview public.
  • Health check.
  • Webhook từ bên thứ ba.

Hệ thống cần phân biệt:

Unauthenticated:
  không biết ai.

Authenticated:
  biết user/service là ai.

Đừng giả định mọi request đều có user.

Code tốt thường có khái niệm:

current_user có thể null.

Và route nhạy cảm phải yêu cầu authenticated.

---

36.35. Account linking

User có thể đăng nhập bằng nhiều cách:

Email/password.
Google.
Microsoft.
GitHub.

Account linking là gắn nhiều identity provider vào một user nội bộ.

Ví dụ:

internal user_id = user_9
linked identities:
  google_sub = ...
  microsoft_sub = ...
  password_login = enabled

Cẩn thận:

Không nên chỉ dựa vào email trùng là tự động merge account.

Vì email verification, provider, tenant, school domain có thể phức tạp.

Account linking cần flow an toàn:

  • User đang đăng nhập.
  • User xác nhận thêm provider.
  • Provider xác minh identity.
  • App link vào account hiện tại.

---

36.36. Email verification

Nếu app dùng email/password, thường cần verify email.

Vì nếu không verify, user có thể đăng ký bằng email của người khác.

Luồng:

1. User đăng ký.
2. App gửi email verification link.
3. User bấm link.
4. App đánh dấu email_verified=true.

Một số hành động có thể yêu cầu email verified:

  • Nộp bài chính thức.
  • Nhận thông báo.
  • Reset password.
  • Mời thành viên.
  • Thanh toán.

Tùy sản phẩm.

Verification token cũng nên:

  • Hết hạn.
  • Dùng một lần.
  • Không dễ đoán.

---

36.37. Auth event và audit

Authentication nên ghi lại các event quan trọng:

login_succeeded
login_failed
logout
password_changed
password_reset_requested
password_reset_completed
mfa_enabled
mfa_failed
session_revoked

Với admin/teacher, audit càng quan trọng.

Audit giúp:

  • Điều tra sự cố.
  • Phát hiện đăng nhập lạ.
  • Hỗ trợ user.
  • Đáp ứng yêu cầu compliance.

Nhưng audit log không nên chứa password/token thật.

Không bao giờ log:

password
access token
refresh token
reset token
session secret

Log để điều tra, không phải để tạo lỗ hổng mới.

---

36.38. Những lỗi authentication phổ biến

Lỗi 1:

Lưu password plain text.

Lỗi 2:

Dùng JWT nhưng token sống quá lâu và không revoke được.

Lỗi 3:

Nhét dữ liệu nhạy cảm vào JWT.

Lỗi 4:

Lưu token dài hạn trong localStorage mà không nghĩ XSS.

Lỗi 5:

Cookie auth nhưng không nghĩ CSRF/SameSite.

Lỗi 6:

Password reset token quá lâu hoặc dùng nhiều lần.

Lỗi 7:

Không rate limit login.

Lỗi 8:

Tự code OAuth flow sai.

Lỗi 9:

Nhầm id_token với access token.

Lỗi 10:

Đăng nhập xong coi như user được làm mọi thứ.

Lỗi 10 chính là cầu nối sang chương authorization.

---

36.39. Bảng chọn nhanh

| Tình huống | Cách làm thường hợp | |---|---| | Web app cùng domain | Session + HttpOnly Secure SameSite cookie | | SPA cùng backend rõ ràng | Cookie session hoặc token flow cẩn thận | | Mobile app | Access token + refresh token | | Enterprise SSO | OIDC với Google/Microsoft/Okta/Auth0/etc | | Muốn logout/revoke dễ | Session hoặc refresh token stateful | | API gọi từ service/integration | API key/JWT/mTLS tùy mức bảo mật | | Token cần sống lâu | Dùng refresh token, access token ngắn | | Teacher/admin quyền cao | MFA + audit + session management | | Team nhỏ, auth không phải core | Dùng auth provider có sẵn | | Tự làm auth | Phải xử lý password, reset, rate limit, session, MFA nếu cần |

---

36.40. Tóm tắt bằng AI Judge

Trong AI Judge:

Authentication trả lời:

Request này là của student nào, teacher nào, admin nào, hay service nào?

Nếu là web app:

Session cookie HttpOnly có thể là lựa chọn rất thực dụng.

Nếu có mobile/API nhiều client:

Access token + refresh token có thể hợp hơn.

Nếu dùng đăng nhập Google/Microsoft cho trường học:

OIDC là con đường chuẩn.

Sau khi authentication xong, app có:

current_user = user_9

Nhưng app vẫn chưa được kết luận:

user_9 được xem assignment này.
user_9 được sửa điểm.
user_9 được xem submission của người khác.

Những câu đó thuộc authorization.

---

36.41. Kết luận của chương

Authentication là nền móng của bảo mật ứng dụng.

Nó không chỉ là form login.

Nó gồm cách xác minh identity, cách giữ phiên đăng nhập, cách cấp token, cách revoke, cách reset password, cách bảo vệ session, và cách tích hợp identity provider.

Thông điệp cần nhớ:

> Authentication xác nhận bạn là ai. Authorization quyết định bạn được làm gì.

Session/cookie không lỗi thời.

JWT không tự động tốt hơn.

OAuth2 không phải chỉ là login.

OIDC mới là lớp identity dùng cho login với provider.

Access token nên ngắn hạn.

Refresh token cần bảo vệ kỹ.

Với sản phẩm thật, đặc biệt là hệ thống có teacher/admin hoặc dữ liệu học sinh, dùng auth provider tốt thường là lựa chọn rất đáng cân nhắc.

Ở chương tiếp theo, ta sẽ nói về authorization: phân quyền là gì, RBAC/ABAC/ACL khác nhau ra sao, object-level permission vì sao khó, và cách nghĩ về quyền trước khi code.