Thiết kế xác thực với Amazon Cognito: 6 điểm mấu chốt về Passkey, MFA và Session (Cognito認証設計)
Phân tích 6 giới hạn kiến trúc khi thiết kế luồng xác thực trên AWS Cognito: từ idle timeout, khóa màn hình, SMS MFA, xác thực OTP đặt lại mật khẩu đến Passkey và Admin API.
✍️ Tác giả nguồn:
gerrard15(thuộc tổ chức claves) trên diễn đàn công nghệ Qiita Nhật Bản.
🔗 Tiêu đề gốc: Amazon Cognitoの認証設計で押さえたい6つの注意点|パスキー・MFA・セッション管理
🌐 Bài gốc: Qiita · 29d62c11c26945570e82, đăng và cập nhật ngày 16/10/2026, đọc ngày 17/10/2026.
📝 Biên soạn: Lược thuật kết hợp phân tích kiến trúc, đối chiếu tài liệu AWS & OWASP, và góc nhìn thiết kế yêu cầu dành cho Kỹ sư cầu nối (BrSE) và lập trình viên Full-stack. Câu đàm thoại tiếng Nhật là ví dụ luyện tập tự soạn, không phải trích dẫn nguyên văn.
📌 Điểm nhấn cốt lõi (Key Takeaways)
- Cognito không tự quản lý Idle Timeout: Thời hạn của JSON Web Token (JWT) là cố định; việc đăng xuất khi người dùng không thao tác phải do ứng dụng tự theo dõi mốc thời gian và từ chối tại tầng API nghiệp vụ.
- Xác thực lại (Re-authentication) dễ phát sinh tin nhắn rác: Luồng
AdminInitiateAuthchuẩn sẽ kích hoạt gửi SMS nếu người dùng bậtSMS_MFA, không thể chỉ dùng để đối chiếu mật khẩu mà bỏ qua bước gửi mã. - Không thể xác minh độc lập mã đặt lại mật khẩu: API
ConfirmForgotPasswordbắt buộc gửi đồng thời mã xác nhận kèm mật khẩu mới. Nếu muốn xác thực mã trước khi cho phép nhập mật khẩu mới, phải tự xây luồng OTP riêng. - Passkey trong luồng
USER_AUTHchuẩn vẫn đòiUSERNAME: Muốn lược bỏ ô nhập tên đăng nhập, ứng dụng bắt buộc phải lưu trữ danh tính từ phiên trước (qua Cookie/Local Storage). - Đổi mật khẩu sau Passkey cần phân biệt quyền hạn: Người dùng đã có mật khẩu không thể dùng
ChangePasswordmà bỏ qua mật khẩu cũ; nếu dùngAdminSetUserPasswordphía backend thì toàn bộ trách nhiệm phân quyền phải do ứng dụng đảm nhận.
1. Ranh giới giữa Amazon Cognito và Ứng dụng nghiệp vụ
Khi phát triển các ứng dụng Web doanh nghiệp sử dụng Amazon Cognito User Pools, nhiều nhóm dự án thường mặc định rằng: “Chỉ cần chọn dịch vụ xác thực được quản lý của AWS là toàn bộ yêu cầu bảo mật từ đăng nhập, khóa màn hình đến thu hồi phiên sẽ tự động được đáp ứng.”
Tuy nhiên, trong bài chia sẻ của tác giả gerrard15, thực tế triển khai cho thấy có sự phân định rất rõ ràng giữa chuẩn OAuth2/OIDC mà Cognito cung cấp và trạng thái phiên (Session State) mà ứng dụng phải tự quản trị. Nếu BrSE không nắm rõ ranh giới này ngay từ giai đoạn định nghĩa yêu cầu (要件定義), nhóm phát triển rất dễ rơi vào bẫy trễ tiến độ khi phát hiện ra các luồng nghiệp vụ đặc thù không được Cognito hỗ trợ sẵn.
Cognitoの標準仕様で対応できる範囲と、アプリ側で状態管理が必要な要件を明確に切り分けましょう。
Bản dịch: Chúng ta hãy bóc tách rõ ràng giữa phạm vi mà quy cách chuẩn của Cognito có thể đáp ứng và những yêu cầu đòi hỏi phải quản lý trạng thái ở phía ứng dụng.
2. Phân tích 6 điểm mấu chốt trong thiết kế xác thực
① Đăng xuất khi không thao tác (無操作ログアウト) và Thu hồi token tức thì
- Vấn đề thực tế: Khách hàng yêu cầu “Sau 15 phút người dùng không thao tác, hệ thống phải tự động đăng xuất và vô hiệu hóa quyền truy cập”.
- Giới hạn của Cognito: Access Token và ID Token của Cognito là stateless JWT có thời hạn cố định (expiration). Kể cả khi gọi API thu hồi Refresh Token (
GlobalSignOuthoặcRevokeToken), các Access Token đã phát hành vẫn có hiệu lực cho đến khi hết hạn tự nhiên nếu API backend chỉ kiểm tra chữ ký số (signature) mà không kiểm tra trạng thái thu hồi. - Giải pháp kiến trúc: Tuân thủ hướng dẫn OWASP Session Management Cheat Sheet (Idle Timeout): Ứng dụng phải lưu mốc thời gian thao tác gần nhất (Last Activity Timestamp) ở backend session/Redis và kiểm tra trước mỗi request nghiệp vụ.
② Khóa màn hình và bảo toàn dữ liệu đang nhập (画面ロック・作業保持)
- Vấn đề: Khi rời bàn làm việc, màn hình bị khóa nhưng người dùng không muốn mất form dữ liệu đang điền dở.
- Giải pháp: Khóa màn hình là trạng thái UI kết hợp với từ chối request API ở backend. Tuyệt đối không chỉ che màn hình bằng modal ở frontend, vì kẻ tấn công vẫn có thể dùng token trong DevTools để gọi API. Khi mở khóa, ứng dụng dùng Cognito để xác thực lại đúng tài khoản ban đầu rồi mới khôi phục phiên làm việc.
③ Xác nhận lại mật khẩu mà không gửi lại SMS (SMSなしのパスワード再確認)
- Vấn đề: Khi thực hiện thao tác nhạy cảm (như chuyển tiền hoặc đổi thông tin cá nhân), ứng dụng muốn yêu cầu người dùng nhập lại mật khẩu để xác thực.
- Giới hạn: Nếu người dùng đã kích hoạt
SMS_MFA, việc gọi luồng chuẩnAdminInitiateAuthsẽ tự động kích hoạt gửi tin nhắn SMS chứa mã OTP. Không thể chặn SMS chỉ bằng cách không hiển thị màn hình nhập mã OTP trên frontend! - Giải pháp: Nếu dùng
Custom SMS Sender Lambda Trigger, trigger này sẽ can thiệp vào toàn bộ SMS của User Pool (gồm cả đăng ký và quên mật khẩu). Vì vậy, cần cân nhắc dùng WebAuthn/Passkey hoặc đánh giá xem thao tác đó có thực sự cần bước xác thực thứ hai hay chỉ cần kiểm tra session hợp lệ.
④ Kiểm tra mã OTP đặt lại mật khẩu trước khi cho nhập mật khẩu mới
- Vấn đề: Thiết kế UI thường chia làm 2 bước: Màn hình 1 nhập mã OTP gửi qua email/SMS; sau khi mã đúng mới chuyển sang Màn hình 2 cho nhập mật khẩu mới.
- Giới hạn của Cognito: API ConfirmForgotPassword là atomic API: đòi hỏi phải gửi đồng thời cả
ConfirmationCodeVÀPassword. Cognito không cung cấp API chỉ để “thử kiểm tra xem mã có đúng hay không”. - Giải pháp: Hoặc điều chỉnh UI để gửi cả hai thông tin ở bước cuối cùng, hoặc nếu bắt buộc phải tách bước thì backend phải tự sinh mã OTP nội bộ, xác minh độc lập, rồi mới gọi
AdminSetUserPassword.
[Luồng chuẩn Cognito]
Màn hình 1: Nhập OTP (chưa kiểm tra)
↓
Màn hình 2: Nhập Mật khẩu mới
↓
Gửi đồng thời cả hai qua ConfirmForgotPassword -> Thành công / Thất bại
⑤ Đăng nhập Passkey không cần nhập Username
- Vấn đề: Người dùng muốn trải nghiệm đăng nhập không mật khẩu (Passkey/FIDO2) bằng cách chạm vân tay mà không cần gõ trước địa chỉ email hoặc tên đăng nhập.
- Giới hạn: Trong luồng chuẩn
USER_AUTHcủa Cognito, APIInitiateAuthbắt buộc phải có tham sốUSERNAME. - Giải pháp: Để mang lại trải nghiệm không cần gõ, ứng dụng client phải lưu trữ username/email đã từng đăng nhập vào Secure Cookie hoặc Local Storage để tự động đính kèm vào payload gửi lên API.
⑥ Đổi mật khẩu sau khi xác thực bằng Passkey
- Vấn đề: Người dùng đăng nhập bằng Passkey, sau đó vào trang cá nhân để thiết lập hoặc cập nhật mật khẩu.
- Giới hạn: API
ChangePasswordbắt buộc người dùng phải cung cấpPreviousPassword. Nếu người dùng quên mật khẩu cũ hoặc tài khoản thuần Passkey, API này sẽ từ chối. - Giải pháp: Backend phải sử dụng API quản trị AdminSetUserPassword với cờ
Permanent: true. Khi dùng API này, backend hoàn toàn chịu trách nhiệm xác minh danh tính và quyền hạn của người gọi (theo khuyến nghị OWASP Re-authentication).
3. Kiến trúc luồng xác thực tổng thể
Dưới đây là sơ đồ ranh giới trách nhiệm giữa Ứng dụng và AWS Cognito do BrSE.vn tổng hợp:
Sơ đồ do BrSE.vn tự vẽ, phát hành theo CC0 1.0 Universal. Tệp nằm trong repository; đây không phải screenshot hoặc tài nguyên lấy từ Qiita.
4. Minh họa mã nguồn: Kiểm soát thu hồi Token tại Backend
Ví dụ TypeScript (Node.js/Express) minh họa cơ chế kiểm tra thời gian không thao tác (Idle Timeout) và danh sách thu hồi (Revocation Cache) trước khi ủy quyền cho Access Token:
import { Request, Response, NextFunction } from 'express';
interface UserSessionState {
userId: string;
lastActiveAt: number; // Epoch timestamp in milliseconds
isRevoked: boolean;
}
// Giả lập kho lưu trữ trạng thái phiên (trong thực tế dùng Redis hoặc DynamoDB)
const sessionStore = new Map<string, UserSessionState>();
const IDLE_TIMEOUT_MS = 15 * 60 * 1000; // 15 phút
export function enforceIdleTimeoutAndRevocation(
req: Request,
res: Response,
next: NextFunction
): void {
const sessionId = req.headers['x-session-id'] as string;
if (!sessionId) {
res.status(401).json({ error: 'SESSION_MISSING', message: 'Thiếu định danh phiên' });
return;
}
const session = sessionStore.get(sessionId);
const now = Date.now();
if (!session) {
res.status(401).json({ error: 'SESSION_NOT_FOUND', message: 'Phiên không tồn tại' });
return;
}
// 1. Kiểm tra trạng thái đã bị thu hồi từ trước
if (session.isRevoked) {
res.status(401).json({ error: 'TOKEN_REVOKED', message: 'Phiên làm việc đã bị hủy bỏ' });
return;
}
// 2. Kiểm tra thời gian không thao tác (Idle Timeout)
if (now - session.lastActiveAt > IDLE_TIMEOUT_MS) {
session.isRevoked = true; // Đánh dấu thu hồi
res.status(401).json({ error: 'IDLE_TIMEOUT', message: 'Đã hết thời gian thao tác, vui lòng đăng nhập lại' });
return;
}
// Cập nhật mốc thời gian thao tác mới nhất
session.lastActiveAt = now;
next();
}
Lưu ý: Mã nguồn được thiết kế chạy cục bộ nhằm mục đích minh họa nguyên lý xác thực; không kết nối trực tiếp cơ sở dữ liệu production.
5. Kinh nghiệm thực chiến dành cho BrSE
- Làm rõ tài liệu SRS trước khi chốt kiến trúc: Khi khách hàng Nhật yêu cầu “Xác thực hai bước” hoặc “Khóa màn hình sau X phút”, BrSE cần hỏi rõ: Có chấp nhận gửi mã xác thực kèm mật khẩu ở bước cuối cùng không? hay Bắt buộc phải kiểm tra mã OTP độc lập trước khi mở form mật khẩu mới?
- Dự toán chi phí triển khai bổ sung: Nếu các yêu cầu không khớp với quy cách chuẩn của Cognito, nhóm phát triển sẽ phải tốn thêm công sức xây dựng dịch vụ quản lý session phụ trợ (Redis, Lambda Triggers, cơ chế chống CSRF). Cần đưa chi phí này vào bảng phân tích tác động (Impact Analysis) trước khi cam kết tiến độ.
Cognitoの標準仕様に合わせて画面フローを調整できれば、追加実装のコストを抑えることが可能です。
Bản dịch: Nếu chúng ta có thể điều chỉnh luồng màn hình phù hợp với quy cách chuẩn của Cognito, dự án hoàn toàn có thể tiết kiệm được chi phí phát triển bổ sung.
📚 Sổ tay thuật ngữ IT & Hán Việt (重要IT用語集)
| Thuật ngữ (Kanji) | Furigana / Romaji | Âm Hán Việt | Ý nghĩa tiếng Việt | Cấp độ |
|---|---|---|---|---|
| 認証設計 | にんしょうせっけい |
NHẬN CHỨNG THIẾT KẾ | Thiết kế cơ chế xác thực | Technical |
| 要件定義 | ようけんていぎ |
YẾU KIỆN ĐỊNH NGHĨA | Định nghĩa yêu cầu phần mềm | Technical |
| 無操作 | むそうさ |
VÔ THAO TÁC | Không có thao tác (Idle) | N2 |
| 失効 | しっこう |
THẤT HIỆU | Hết hiệu lực, thu hồi (Revoke/Invalidate) | N1 |
| 保持 | ほじ |
BẢO TRÌ | Giữ lại, duy trì (Retain/Preserve) | N2 |
| 認可 | にんか |
NHẬN KHẢ | Phân quyền, cấp phép (Authorization) | Technical |
| 再認証 | さいにんしょう |
TÁI NHẬN CHỨNG | Xác thực lại (Re-authentication) | Technical |
| 制約 | せいやく |
CHẾ ƯỚC | Ràng buộc, giới hạn quy cách | N2 |
| 配送制御 | はいそうせいぎょ |
PHỐI TỐNG CHẾ NGỰ | Điều phối gửi tin/thư (Delivery control) | Technical |
| 事前検証 | じぜんけんしょう |
SỰ TIỀN KIỂM CHỨNG | Kiểm tra, xác minh trước | N2 |
📐 Ngữ pháp tiếng Nhật IT bổ trợ (文法解説)
- 〜に収まる (
N2): Nằm gọn trong…, vừa vặn trong phạm vi…- Cấu trúc:
Danh từ + に収まる - Ngữ cảnh IT: 標準仕様に収まる = Vừa vặn trong đặc tả tiêu chuẩn của dịch vụ (không cần custom thêm).
- Cấu trúc:
- 〜を補う (
N2): Bổ sung, bù đắp cho phần còn thiếu.- Cấu trúc:
Danh từ + を補う - Ngữ cảnh IT: アプリ側で何を補うべきか = Cần bổ sung thêm xử lý gì ở phía ứng dụng.
- Cấu trúc:
- 〜だけでなく〜も (
N3): Không những… mà còn…- Cấu trúc:
A だけでなく B も - Ngữ cảnh IT: 通常ログインだけでなく再設定も = Không chỉ đăng nhập thông thường mà cả luồng đặt lại mật khẩu.
- Cấu trúc:
- 〜を踏まえたうえで (
N1): Dựa trên cơ sở…, sau khi đã cân nhắc thấu đáo…- Cấu trúc:
Danh từ + を踏まえたうえで - Ngữ cảnh IT: コストを踏まえたうえで判断する = Đưa ra quyết định sau khi đã cân nhắc toàn diện chi phí.
- Cấu trúc: