Mở đầu — vì sao bài này quan trọng
Hãy tưởng tượng bạn đang dùng một app du lịch tên là TripVN. App này muốn tự động đọc Google Calendar của bạn để gợi ý lịch trình sao cho không trùng với các cuộc họp. Câu hỏi đặt ra: làm sao TripVN xem được Calendar của bạn?
Cách "ngây thơ" nhất là TripVN hỏi bạn: "Cho mình xin email và mật khẩu Google của bạn." Bạn nghĩ thử xem, điều này nguy hiểm đến mức nào. Khi đưa mật khẩu Google, bạn không chỉ trao quyền đọc Calendar — bạn trao TOÀN BỘ tài khoản: Gmail, Drive, Photos, YouTube, thậm chí quyền đổi mật khẩu. TripVN có thể lưu mật khẩu đó ở đâu đó, bị hacker đánh cắp, hoặc nhân viên TripVN tò mò đọc thư của bạn. Đó là lý do OAuth 2.0 ra đời.
OAuth 2.0 giải đúng một bài toán cốt lõi: "App A muốn truy cập data của tôi ở App B, mà KHÔNG cần biết mật khẩu B của tôi." Thay vì trao chìa khóa nhà, bạn cấp cho khách một thẻ ra vào tạm thời, chỉ mở được đúng một căn phòng, và có thể thu hồi bất cứ lúc nào.
Là một BA (Business Analyst), bạn sẽ gặp OAuth liên tục: khi viết spec cho tính năng "Đăng nhập bằng Google", khi tích hợp app công ty với hệ thống của đối tác, khi làm việc với payment gateway hay CRM. Bạn không cần code OAuth, nhưng bạn PHẢI hiểu các luồng (flow) khác nhau để chọn đúng luồng cho đúng tình huống, và để trao đổi chính xác với dev và đối tác. Chọn sai flow là một lỗi thiết kế tốn kém — sửa rất đau. Bài này sẽ giúp bạn nắm chắc ba flow quan trọng nhất: Authorization Code, Client Credentials, và Device.
Khái niệm cốt lõi
Bốn vai trò trong OAuth
Trước khi nói về flow, bạn cần thuộc lòng bốn nhân vật. Quay lại ví dụ TripVN và Google Calendar:
- Resource Owner (chủ tài nguyên): chính là bạn — người sở hữu data (Calendar).
- Client (ứng dụng): TripVN — app muốn truy cập data của bạn.
- Authorization Server (máy chủ ủy quyền): hệ thống của Google chịu trách nhiệm xác thực bạn và phát "vé". Đây là nơi bạn nhìn thấy màn hình "TripVN muốn xem Lịch của bạn — Cho phép / Từ chối".
- Resource Server (máy chủ tài nguyên): API Google Calendar — nơi data thực sự nằm, và nó kiểm tra "vé" trước khi trả data.
Access Token và Refresh Token
"Vé" mà OAuth phát ra gọi là access token — một chuỗi ký tự đại diện cho quyền truy cập đã được cấp. Khi TripVN gọi API Calendar, nó đính kèm access token trong header Authorization: Bearer <token>. Resource Server thấy token hợp lệ thì trả data.
Điểm quan trọng: access token có scope (phạm vi) và thời hạn. Scope quy định token này làm được gì — ví dụ calendar.readonly chỉ cho đọc, không cho xóa lịch. Thời hạn thường ngắn, ví dụ 1 giờ. Hết hạn thì token vô dụng, đây là cơ chế an toàn: kể cả token bị lộ, kẻ xấu chỉ dùng được trong thời gian ngắn.
Refresh token là một loại vé đặc biệt, sống lâu hơn, dùng để xin access token mới khi cái cũ hết hạn — mà không bắt người dùng đăng nhập lại. Đây là lý do bạn đăng nhập Google một lần rồi cả tháng không phải nhập lại.
Vì sao có nhiều "flow"?
OAuth không phải một quy trình duy nhất. Tùy vào loại client và bối cảnh, ta dùng flow khác nhau. Câu hỏi quyết định là: Có một con người đang ngồi trước màn hình để bấm "Cho phép" không? Client có thể giữ bí mật (client secret) an toàn không? Thiết bị có trình duyệt và bàn phím tử tế không? Ba flow dưới đây trả lời cho ba tình huống phổ biến nhất.
Flow 1 — Authorization Code
Đây là flow phổ biến và an toàn nhất, dùng khi có người dùng thật và client là một web app hoặc mobile app. Diễn biến:
- Người dùng bấm "Kết nối Google" trong TripVN.
- TripVN chuyển hướng (redirect) trình duyệt sang Authorization Server của Google, kèm theo
client_id,scope,redirect_uri. - Người dùng đăng nhập Google (trên chính trang Google, TripVN KHÔNG thấy mật khẩu) và bấm "Cho phép".
- Google redirect ngược về
redirect_uricủa TripVN, kèm một authorization code — một mã tạm thời, dùng một lần. - TripVN (ở phía backend) đổi code này lấy access token, bằng cách gọi Google kèm
client_secret. Bước trao đổi này diễn ra server-to-server, an toàn. - TripVN dùng access token gọi API Calendar.
client_secret mà chỉ backend mới giữ. Đây là thiết kế tách "thứ đi qua nơi nguy hiểm" khỏi "thứ thực sự có quyền".Với mobile app và Single Page App không giữ được secret, người ta dùng biến thể PKCE (Proof Key for Code Exchange) — thêm một cặp mã ngẫu nhiên thay cho client secret. Bạn chỉ cần nhớ: hỏi dev "Có dùng PKCE không?" là một câu hỏi rất chuyên nghiệp khi review spec mobile.
Flow 2 — Client Credentials
Flow này dùng khi KHÔNG có người dùng — máy nói chuyện với máy (machine-to-machine). Không có ai để bấm "Cho phép". Client tự đại diện cho chính nó, không đại diện cho người dùng nào.
Diễn biến rất gọn: Client gửi client_id + client_secret thẳng tới Authorization Server, nhận về access token. Không có redirect, không có màn hình đăng nhập.
Ví dụ điển hình: hệ thống kế toán nội bộ của công ty gọi API ngân hàng mỗi đêm để đối soát giao dịch. Không có con người ngồi bấm — đây là tiến trình tự động (cron job, backend service). Vì secret được giữ trong server an toàn, flow này dùng client_secret trực tiếp là hợp lý.
Flow 3 — Device Authorization (Device Flow)
Flow này dành cho thiết bị không có bàn phím hoặc trình duyệt tử tế: Smart TV, loa thông minh, máy chơi game, thiết bị IoT. Bạn thử tưởng tượng gõ mật khẩu Google bằng remote TV — cực hình.
Diễn biến thông minh: Thiết bị hiển thị một mã ngắn và một URL (ví dụ: "Vào tv.youtube.com/activate và nhập mã ABCD-1234"). Bạn lấy điện thoại — thứ có bàn phím tốt — mở URL, nhập mã, đăng nhập và bấm "Cho phép". Trong lúc đó, TV liên tục hỏi (poll) Authorization Server: "Người dùng cho phép chưa?". Khi bạn bấm xong, lần poll tiếp theo TV nhận được access token và bắt đầu phát video.
Điểm hay: việc nhập thông tin nhạy cảm chuyển sang thiết bị phù hợp, còn thiết bị "ngốc" chỉ cần hiển thị mã và chờ.
Tình huống thực tế
Tình huống 1 — Sàn TMĐT tích hợp ví điện tử (Authorization Code)
Một sàn thương mại điện tử giả định ở TP.HCM, gọi là ChợViệt, muốn cho phép khách hàng "liên kết ví MoMo" để thanh toán một chạm. Yêu cầu nghiệp vụ: khi mua hàng, khách không phải nhập lại thông tin ví mỗi lần.
BA của ChợViệt ngồi với đối tác và xác định: đây có người dùng thật, có trình duyệt/app, cần khách bấm đồng ý. Vậy đúng là Authorization Code flow. Khách bấm "Liên kết MoMo", được chuyển sang trang MoMo, đăng nhập bằng chính tài khoản MoMo (ChợViệt không bao giờ thấy mật khẩu hay mã PIN ví), đồng ý với scope payment và read_profile. MoMo trả code, backend ChợViệt đổi lấy access token và lưu lại refresh token để các lần mua sau không phải liên kết lại.
Bài học: Khi nghiệp vụ có chữ "người dùng đồng ý liên kết tài khoản của họ", gần như chắc chắn là Authorization Code. BA cần làm rõ trong spec: scope nào, redirect_uri là URL nào, và xử lý ra sao khi access token hết hạn (dùng refresh token).
Tình huống 2 — Đối soát giao dịch ban đêm (Client Credentials)
Vẫn ChợViệt, nhưng một tính năng khác: mỗi đêm 2 giờ sáng, hệ thống tài chính tự động gọi API của ngân hàng đối tác để lấy danh sách giao dịch trong ngày và đối soát với đơn hàng. Một kế toán hỏi BA: "Cái này dùng OAuth giống liên kết MoMo không?"
BA trả lời: không. Ở đây không có người dùng nào ngồi bấm "Cho phép" lúc 2 giờ sáng. Đây là máy nói chuyện với máy, đại diện cho công ty ChợViệt chứ không đại diện cho một khách hàng cụ thể. Vậy đúng flow là Client Credentials. Ngân hàng cấp cho ChợViệt một cặp client_id + client_secret, lưu an toàn trong vault của hệ thống. Cron job dùng cặp này xin token và gọi API.
Bài học: Dấu hiệu nhận biết Client Credentials là "tự động", "background job", "system-to-system", "không có UI cho người dùng". Một lỗi BA hay mắc: cố nhét flow Authorization Code vào tình huống này, rồi loay hoay không biết "người dùng" là ai để bấm đồng ý. Nếu không có con người trong vòng lặp, đừng chọn flow cần con người.
Tình huống 3 — App học trên Smart TV (Device Flow)
Một startup edtech ở Hà Nội giả định, gọi là HọcTV, làm app khóa học chạy trên Android TV để cả gia đình học tiếng Anh trên màn hình lớn. BA nhận yêu cầu: "Cho người dùng đăng nhập tài khoản HọcTV trên TV."
BA thử hình dung trải nghiệm: gõ email và mật khẩu phức tạp bằng remote TV, di chuyển con trỏ từng ký tự — quá tệ, tỉ lệ bỏ cuộc cao. Giải pháp đúng là Device Flow. TV hiển thị: "Mở hoctv.vn/kichhoat trên điện thoại và nhập mã 8K2P-7Q2M". Người dùng dùng điện thoại đăng nhập trong vài giây, TV tự động vào được tài khoản.
Bài học: Khi thiết bị "khó nhập liệu" (TV, loa, IoT, thiết bị POS đơn giản), Device Flow là lựa chọn. Trong spec, BA cần mô tả rõ: thời hạn của mã ngắn (thường 5–15 phút), thông điệp hiển thị, và hành vi khi người dùng không kích hoạt kịp (mã hết hạn, hiện mã mới).
Hướng dẫn từng bước
Đây là quy trình giúp BA chọn đúng flow khi viết spec hoặc làm việc với đối tác:
- Xác định có người dùng thật hay không. Hỏi: "Có một con người sẽ ngồi bấm Cho phép không?" Nếu KHÔNG → gần như chắc chắn Client Credentials. Nếu CÓ → đi tiếp.
- Xác định loại thiết bị/client. Nếu là thiết bị khó nhập liệu (TV, loa, IoT) → Device Flow. Nếu là web app hoặc mobile app bình thường → Authorization Code.
- Với Authorization Code, xác định client có giữ được secret không. Web app có backend → giữ được secret. Mobile/SPA không có backend → dùng Authorization Code + PKCE. Ghi rõ điều này để dev không hiểu nhầm.
- Liệt kê scope cần thiết theo nguyên tắc tối thiểu. Chỉ xin đúng quyền cần dùng. Cần đọc lịch thì xin
calendar.readonly, đừng xin quyền ghi. Scope thừa là rủi ro bảo mật và làm người dùng e ngại khi thấy màn hình đồng ý.
- Làm rõ redirect_uri (với Authorization Code). Đây là URL Authorization Server sẽ gửi code/token về. Phải đăng ký trước với nhà cung cấp. Sai redirect_uri là lỗi tích hợp số một.
- Thiết kế xử lý token hết hạn. Quyết định: dùng refresh token để gia hạn ngầm, hay bắt người dùng đăng nhập lại. Ghi rõ thời hạn access token và refresh token trong spec.
- Lập kế hoạch thu hồi (revoke). Người dùng phải có nút "Ngắt kết nối" để thu hồi quyền. Đây là một yêu cầu nghiệp vụ thường bị quên, nhưng cực kỳ quan trọng cho niềm tin của người dùng.
Lỗi thường gặp & mẹo
Lỗi 1: Nhầm OAuth với đăng nhập. OAuth 2.0 nguyên gốc là về ủy quyền (authorization) — "cho phép làm gì", không phải xác thực danh tính (authentication) — "bạn là ai". Thứ chuẩn hóa việc đăng nhập là OpenID Connect (OIDC), một lớp xây trên OAuth. Khi nghiệp vụ là "Đăng nhập bằng Google", về kỹ thuật bạn đang dùng OIDC chứ không chỉ OAuth thuần. Mẹo: trong spec, phân biệt rõ "lấy quyền truy cập data" với "xác định danh tính người dùng".
Lỗi 2: Dùng Client Credentials cho luồng có người dùng. Vì Client Credentials gọn gàng, dev đôi khi lạm dụng nó cho cả tình huống có người dùng, dẫn đến mọi người dùng chia sẻ chung một quyền của hệ thống — không phân biệt được ai làm gì, và vi phạm nguyên tắc tối thiểu quyền. Nhớ: Client Credentials chỉ dành cho machine-to-machine.
Lỗi 3: Coi access token như mật khẩu vĩnh viễn. Access token là tạm thời và có scope. Đừng thiết kế hệ thống dựa trên giả định token sống mãi. Luôn có phương án xử lý khi token hết hạn (lỗi 401 Unauthorized).
Lỗi 4: Bỏ qua việc thu hồi. Nếu người dùng đổi ý hoặc thiết bị bị mất, phải có cách thu hồi token. Spec nào liên quan OAuth mà thiếu phần "ngắt kết nối / thu hồi" là spec chưa hoàn chỉnh.
Mẹo cho BA: Khi đọc tài liệu API của đối tác, hãy tìm mục "OAuth" hoặc "Authentication". Họ sẽ ghi rõ hỗ trợ flow nào (grant_type). Bốn từ khóa cần nhận diện: authorization_code, client_credentials, urn:ietf:params:oauth:grant-type:device_code (Device), và refresh_token. Nhận ra được các từ này, bạn đọc tài liệu nhanh hơn nhiều.
Bài tập thực hành
- Phân loại flow. Với mỗi tình huống, chọn flow đúng và giải thích ngắn:
- Viết mini-spec. Cho tình huống Tình huống 1 (ChợViệt liên kết MoMo), viết một đoạn spec ngắn gồm: flow chọn, danh sách scope (giả định), redirect_uri (giả định), cách xử lý khi access token hết hạn, và cơ chế cho người dùng ngắt kết nối.
- Tìm lỗi. Một dev đề xuất: "Để app TV đăng nhập cho nhanh, mình dùng Client Credentials với một client secret chung cho mọi TV." Hãy chỉ ra ít nhất hai vấn đề với đề xuất này và đề nghị flow đúng.
Tóm tắt
OAuth 2.0 giải bài toán: cho phép một app truy cập data của bạn ở app khác mà không cần trao mật khẩu, thông qua các "vé" tạm thời (access token) có phạm vi và thời hạn rõ ràng. Bốn vai trò cần thuộc: Resource Owner (bạn), Client (app), Authorization Server (nơi phát vé), Resource Server (nơi giữ data).
Ba flow cốt lõi và quy tắc chọn:
- Authorization Code: có người dùng thật, web/mobile app — phổ biến và an toàn nhất, dùng PKCE cho mobile/SPA.
- Client Credentials: máy nói chuyện với máy, không có người dùng — cho background job, system-to-system.
- Device Flow: thiết bị khó nhập liệu như TV, loa, IoT — hiển thị mã ngắn, nhập trên điện thoại.