OAuth2 Simplified
Phần này mô tả OAuth 2.0 ở định dạng được đơn giản hóa để giúp nhà phát triển và nhà cung cấp dịch vụ triển khai giao thức.
I. Roles (Vai trò)
Ứng dụng của bên thứ ba: “Khách hàng”
Ứng dụng khách là ứng dụng đang cố truy cập vào tài khoản của người dùng. Nó cần được sự cho phép của người dùng trước khi nó có thể làm như vậy.
API: “Máy chủ tài nguyên”
Máy chủ tài nguyên là máy chủ API được sử dụng để truy cập thông tin của người dùng.
Máy chủ ủy quyền
Đây là máy chủ trình bày giao diện nơi người dùng chấp thuận hoặc từ chối yêu cầu. Trong các triển khai nhỏ hơn, đây có thể là cùng một máy chủ với máy chủ API, nhưng triển khai quy mô lớn hơn thường sẽ xây dựng nó như một thành phần riêng biệt.
Người dùng: “Chủ sở hữu tài nguyên”
Chủ sở hữu tài nguyên là người đang cấp quyền truy cập vào một phần tài khoản của họ.
II. Tạo ứng dụng
Trước khi bạn có thể bắt đầu quá trình OAuth, trước tiên bạn phải đăng ký một ứng dụng mới với dịch vụ. Khi đăng ký một ứng dụng mới, bạn thường đăng ký thông tin cơ bản như tên ứng dụng, trang web, biểu tượng, v.v. Ngoài ra, bạn phải đăng ký URI chuyển hướng để chuyển hướng người dùng đến cho máy chủ web, trình duyệt hoặc ứng dụng dành cho thiết bị di động.
URI chuyển hướng
Dịch vụ này sẽ chỉ chuyển hướng người dùng đến một URI đã đăng ký, giúp ngăn chặn một số cuộc tấn công. Mọi URI chuyển hướng HTTP phải được bảo vệ bằng bảo mật TLS, do đó dịch vụ sẽ chỉ chuyển hướng đến URI bắt đầu bằng “https”. Điều này ngăn chặn token bị chặn trong quá trình ủy quyền. Ứng dụng gốc có thể đăng ký URI chuyển hướng bằng lược đồ URL tùy chỉnh cho ứng dụng, có thể giống như demoapp://redirect.
Client ID và Secret
Sau khi đăng ký ứng dụng của bạn, bạn sẽ nhận được một client ID và client secret(bí mật khách hàng).
Client ID được coi là thông tin công khai và được sử dụng để tạo URL đăng nhập hoặc được include trong mã nguồn Javascript trên trang.
Client secret phải được giữ bí mật. Nếu ứng dụng được triển khai không thể giữ Client secret, thì Client secret sẽ không được sử dụng và dịch vụ sẽ không cấp Client secret cho các loại ứng dụng này ngay từ đầu.
Ủy quyền
Bước đầu tiên của OAuth 2 là nhận ủy quyền từ người dùng. Đối với các ứng dụng dành cho thiết bị di động hoặc trình duyệt, điều này thường được thực hiện bằng cách hiển thị giao diện do dịch vụ cung cấp cho người dùng.
OAuth 2 cung cấp một số “grant type” cho các trường hợp sử dụng khác nhau. Các grant type được xác định là:
- Mã ủy quyền cho các ứng dụng chạy trên máy chủ web, ứng dụng dành cho thiết bị di động và trình duyệt
- Mật khẩu để đăng nhập bằng tên người dùng và mật khẩu
- Thông tin đăng nhập ứng dụng khách để truy cập ứng dụng
- Implicit trước đó được đề xuất cho các khách hàng không có bí mật, nhưng đã bị thay thế bằng cách sử dụng cấp phép Mã ủy quyền mà không có bí mật.
Mỗi ca sử dụng được mô tả chi tiết bên dưới:
- Ứng dụng máy chủ web
Ứng dụng máy chủ web là loại ứng dụng phổ biến nhất mà bạn gặp phải khi giao dịch với các máy chủ OAuth. Ứng dụng web được viết bằng ngôn ngữ phía máy chủ và chạy trên máy chủ nơi mã nguồn của ứng dụng không public. Điều này có nghĩa là ứng dụng có thể sử dụng Client secret của nó khi giao tiếp với máy chủ ủy quyền, có thể giúp tránh một số hướng tấn công.
Ủy quyền
Tạo liên kết “Đăng nhập” để đưa người dùng đến:
Ví dụ: https://authorization-server.com/auth?response_type=code&client_id=CLIENT_ID&redirect_uri=REDIRECT_URI&scope=photos&state=1234zyx
- response_type = code – Cho biết máy chủ của bạn mong muốn nhận được mã ủy quyền
- client_id – ID khách hàng bạn nhận được khi bạn tạo ứng dụng lần đầu tiên
- redirect_uri – Cho biết URI trả về người dùng sau khi ủy quyền hoàn tất
- scope – Một hoặc nhiều giá trị phạm vi cho biết phần nào của tài khoản người dùng bạn muốn truy cập trạng thái – Một chuỗi ngẫu nhiên do ứng dụng của bạn tạo, mà bạn sẽ xác minh sau
Người dùng thông báo ủy quyền:
Nếu người dùng nhấp vào “Cho phép”, dịch vụ sẽ chuyển hướng người dùng quay lại trang web của bạn bằng mã xác thực:
Ví dụ: https://example-app.com/cb?code=AUTH_CODE_HERE&state=1234zyx
- code – Máy chủ trả về mã ủy quyền trong chuỗi truy vấn
- state – Máy chủ trả về cùng một giá trị trạng thái mà bạn đã chuyển
Trước tiên, bạn nên so sánh giá trị trạng thái này để đảm bảo nó khớp với giá trị bạn đã bắt đầu. Bạn thường có thể lưu trữ giá trị trạng thái trong cookie hoặc session và so sánh giá trị đó khi người dùng quay lại. Điều này đảm bảo điểm cuối chuyển hướng của bạn không thể bị đánh lừa khi cố gắng trao đổi mã ủy quyền tùy ý.
Getting an Access Token
Máy chủ của bạn đổi mã xác thực lấy access token:
POST https://api.authorization-server.com/token
grant_type=authorization_code&
code=AUTH_CODE_HERE&
redirect_uri=REDIRECT_URI&
client_id=CLIENT_ID&
client_secret=CLIENT_SECRET
- grant_type = authorization_code – Loại cấp phép cho luồng này là authorization_code
- code = AUTH_CODE_HERE – Đây là mã bạn nhận được trong chuỗi truy vấn
- redirect_uri = REDIRECT_URI – Phải giống với URI chuyển hướng được cung cấp trong liên kết gốc
- client_id = CLIENT_ID – ID ứng dụng khách nhận được khi bạn tạo ứng dụng lần đầu tiên
- client_secret = CLIENT_SECRET – Vì yêu cầu này được thực hiện từ mã phía máy chủ, bí mật được include
Máy chủ trả lời bằng mã thông báo truy cập và thời gian hết hạn.
{
“access_token”:”RsT5OjbzRn430zqMLgV3Ia”,
“expires_in”:3600
}
hoặc nếu có lỗi
{
“error”:”invalid_request”
}
- Single-Page Apps
Các ứng dụng một trang (hoặc các ứng dụng dựa trên trình duyệt) chạy hoàn toàn trong trình duyệt sau khi tải mã nguồn từ một trang web. Vì toàn bộ mã nguồn có sẵn cho trình duyệt, chúng không thể duy trì tính bảo mật của client secret, vì vậy client secret không được sử dụng trong trường hợp này. Luồng chính xác giống như luồng mã ủy quyền ở trên, nhưng ở bước cuối cùng, mã ủy quyền được trao đổi cho mã thông báo truy cập mà không cần sử dụng bí mật ứng dụng khách.
Ủy quyền
Tạo liên kết “Đăng nhập” để đưa người dùng đến:
https://authorization-server.com/auth?response_type=code& client_id=CLIENT_ID&redirect_uri=REDIRECT_URI&scope=photos&state=1234zyx |
- response_type = code – Cho biết máy chủ của bạn hy vọng nhận được mã ủy quyền
- client_id – ID khách hàng bạn nhận được khi bạn tạo ứng dụng lần đầu tiên
- redirect_uri – Cho biết URI trả về người dùng sau khi ủy quyền hoàn tất
- scope – Một hoặc nhiều giá trị phạm vi cho biết phần nào của tài khoản người dùng bạn muốn truy cập trạng thái – Một chuỗi ngẫu nhiên do ứng dụng của bạn tạo, mà bạn sẽ xác minh sau
Người dùng thấy lời nhắc ủy quyền
Nếu người dùng nhấp vào “Cho phép”, dịch vụ sẽ chuyển hướng người dùng quay lại trang web của bạn bằng mã xác thực:
Ví dụ: https://example-app.com/cb?code=AUTH_CODE_HERE&state=1234zyx
- code – Máy chủ trả về mã ủy quyền trong chuỗi truy vấn
- state – Máy chủ trả về cùng một giá trị trạng thái mà bạn đã chuyển
Trước tiên, bạn nên so sánh giá trị trạng thái này để đảm bảo nó khớp với giá trị bạn đã bắt đầu. Bạn thường có thể lưu trữ giá trị trạng thái trong cookie và so sánh nó khi người dùng quay lại. Điều này đảm bảo điểm cuối chuyển hướng của bạn không thể bị đánh lừa khi cố gắng trao đổi mã ủy quyền tùy ý.
Getting an Access Token
POST https://api.authorization-server.com/token
grant_type=authorization_code&
code=AUTH_CODE_HERE&
redirect_uri=REDIRECT_URI&
client_id=CLIENT_ID&
code_verifier=CODE_VERIFIER
- grant_type = authorization_code – Loại cấp phép cho luồng này là authorization_code
- code = AUTH_CODE_HERE – Đây là mã bạn nhận được trong chuỗi truy vấn
- redirect_uri = REDIRECT_URI – Phải giống với URI chuyển hướng được cung cấp trong liên kết gốc
- client_id = CLIENT_ID – ID ứng dụng khách nhận được khi bạn tạo ứng dụng lần đầu tiên
- Code _verifier = CODE_VERIFIER – Bí mật ngẫu nhiên bạn tạo ra lúc đầu
- Ứng dụng di động
Giống như các ứng dụng dựa trên trình duyệt, các ứng dụng di động cũng không thể duy trì tính bảo mật của client secret (bí mật khách hàng). Do đó, các ứng dụng dành cho thiết bị di động cũng phải sử dụng luồng OAuth không yêu cầu client secret. Có một số lo ngại khác mà ứng dụng dành cho thiết bị di động nên lưu ý để đảm bảo tính bảo mật của luồng OAuth.
Ủy quyền
Tạo nút “Đăng nhập” để đưa người dùng đến ứng dụng gốc của dịch vụ trên điện thoại hoặc trang web di động cho dịch vụ. Trên iPhone, ứng dụng có thể đăng ký lược đồ URI tùy chỉnh như “facebook: //” để ứng dụng Facebook gốc được khởi chạy bất cứ khi nào một URL có giao thức đó được truy cập. Trên Android, ứng dụng có thể đăng ký các mẫu khớp URL sẽ khởi chạy ứng dụng gốc nếu URL khớp với mẫu được truy cập.
Sử dụng ứng dụng gốc của dịch vụ
Nếu người dùng đã cài đặt ứng dụng Facebook gốc, hãy hướng họ đến URL sau:
Ví dụ: fbauth2://authorize?response_type=code&client_id=CLIENT_ID &redirect_uri=REDIRECT_URI&scope=email&state=1234zyx
- response_type = code – cho biết máy chủ của bạn hy vọng nhận được mã ủy quyền
- client_id = CLIENT_ID – ID ứng dụng khách nhận được khi bạn tạo ứng dụng lần đầu tiên
- redirect_uri = REDIRECT_URI – Cho biết URI trả về người dùng sau khi ủy quyền hoàn tất, chẳng hạn như fb00000000://authorize
- scope = email – Một hoặc nhiều giá trị phạm vi cho biết phần nào của tài khoản người dùng bạn muốn truy cập
- state = 1234zyx – Một chuỗi ngẫu nhiên do ứng dụng của bạn tạo, mà bạn sẽ xác minh sau
Đối với các máy chủ hỗ trợ phần mở rộng PKCE (và nếu bạn đang xây dựng một máy chủ, bạn nên hỗ trợ phần mở rộng PKCE), bạn cũng sẽ bao gồm các tham số sau đây. Đầu tiên, tạo một “bộ kiểm tra mã” là một chuỗi ngẫu nhiên mà ứng dụng lưu trữ cục bộ.
- code_challenge = XXXXXXX – Đây là phiên bản mã hóa base64 của hàm băm sha256 của chuỗi mã xác minh
- code_challenge_method = S256 – Chỉ ra phương pháp băm được sử dụng để tính toán thử thách, trong trường hợp này là sha256.
Lưu ý rằng URI chuyển hướng của bạn có thể trông giống như fb00000000://authorize nơi giao thức là một lược đồ URL tùy chỉnh mà ứng dụng của bạn đã đăng ký với hệ điều hành.
Sử dụng trình duyệt web
Nếu dịch vụ không có ứng dụng gốc, bạn có thể khởi chạy trình duyệt dành cho thiết bị di động với URL ủy quyền web chuẩn. Lưu ý rằng bạn không nên sử dụng chế độ xem web được nhúng trong ứng dụng của riêng mình, vì điều này cung cấp cho người dùng không đảm bảo rằng họ đang thực sự nhập mật khẩu của họ vào trang web của dịch vụ thay vì trang web lừa đảo.
Bạn nên khởi chạy trình duyệt di động gốc hoặc sử dụng “SafariViewController” iOS mới để khởi chạy trình duyệt được nhúng trong ứng dụng của mình. API này đã được thêm vào iOS 9 và cung cấp cơ chế khởi chạy trình duyệt bên trong ứng dụng, cả hai đều hiển thị thanh địa chỉ để người dùng có thể xác nhận họ đang ở đúng trang web và cũng chia sẻ cookie với trình duyệt Safari thực. Nó cũng ngăn ứng dụng kiểm tra và sửa đổi nội dung của trình duyệt, vì vậy có thể được coi là an toàn.
Ví dụ: https://facebook.com/dialog/oauth?response_type=code&client_id=CLIENT_ID&redirect_uri=REDIRECT_URI&scope=email&state=1234zyx
Một lần nữa, nếu dịch vụ hỗ trợ PKCE, thì các thông số đó phải được bao gồm cũng như mô tả ở trên.
- response_type = code – cho biết máy chủ của bạn hy vọng nhận được mã ủy quyền
- client_id = CLIENT_ID – ID ứng dụng khách nhận được khi bạn tạo ứng dụng lần đầu tiên
- redirect_uri = REDIRECT_URI – Cho biết URI trả về người dùng sau khi ủy quyền hoàn tất, chẳng hạn như fb00000000://authorize
- scope = email – Một hoặc nhiều giá trị phạm vi cho biết phần nào của tài khoản người dùng bạn muốn truy cập
- state = 1234zyx – Một chuỗi ngẫu nhiên do ứng dụng của bạn tạo, mà bạn sẽ xác minh sau
Người dùng sẽ thấy lời nhắc ủy quyền
Getting an Access Token
Sau khi nhấp vào “Phê duyệt”, người dùng sẽ được chuyển hướng trở lại ứng dụng của bạn bằng một URL như:
Ví dụ: fb00000000://authorize?code=AUTHORIZATION_CODE&state=1234zyx
Trước tiên, ứng dụng di động của bạn phải xác minh rằng trạng thái tương ứng với trạng thái được sử dụng trong yêu cầu ban đầu và sau đó có thể trao đổi mã ủy quyền cho mã thông báo truy cập.
Trao đổi mã thông báo sẽ giống như trao đổi mã trong trường hợp ứng dụng máy chủ web, ngoại trừ việc bí mật không được gửi. Nếu máy chủ hỗ trợ PKCE, thì bạn sẽ cần bao gồm thông số bổ sung như được mô tả bên dưới.
POST https://api.authorization-server.com/token
grant_type=authorization_code&
code=AUTH_CODE_HERE&
redirect_uri=REDIRECT_URI&
client_id=CLIENT_ID&
code_verifier=VERIFIER_STRING
- grant_type = authorization_code – Loại cấp phép cho luồng này là authorization_code
- code = AUTH_CODE_HERE – Đây là mã bạn nhận được trong chuỗi truy vấn
- redirect_uri = REDIRECT_URI – Phải giống với URI chuyển hướng được cung cấp trong liên kết gốc
- client_id = CLIENT_ID – ID ứng dụng khách nhận được khi bạn tạo ứng dụng lần đầu tiên
- code_verifier = VERIFIER_STRING – Chuỗi văn bản thô mà bạn đã băm trước đó để tạo code_challenge
Máy chủ ủy quyền sẽ xác minh yêu cầu này và trả lại mã thông báo truy cập.
Nếu máy chủ hỗ trợ PKCE, thì máy chủ ủy quyền sẽ nhận ra rằng mã này đã được tạo bằng thử thách mã và sẽ băm nội dung được cung cấp và xác nhận rằng phiên bản được băm tương ứng với chuỗi được băm được gửi trong yêu cầu ủy quyền ban đầu. Điều này đảm bảo tính bảo mật của việc sử dụng luồng mã ủy quyền với các máy khách không hỗ trợ bí mật.
III. Các Grant Types khác
Mật khẩu
OAuth 2 cũng cung cấp loại cấp quyền “mật khẩu” có thể được sử dụng để trao đổi tên người dùng và mật khẩu cho mã thông báo truy cập trực tiếp. Vì điều này rõ ràng yêu cầu ứng dụng thu thập mật khẩu của người dùng, nó chỉ được sử dụng bởi các ứng dụng được tạo bởi chính dịch vụ đó. Ví dụ: ứng dụng Twitter gốc có thể sử dụng grant type này để đăng nhập trên các ứng dụng dành cho thiết bị di động hoặc máy tính để bàn.
Để sử dụng loại cấp mật khẩu, chỉ cần thực hiện yêu cầu POST như sau:
POST https://api.authorization-server.com/token
grant_type=password&
username=USERNAME&
password=PASSWORD&
client_id=CLIENT_ID
- grant_type = password – Loại cấp phép cho luồng này là mật khẩu
- username = USERNAME – Username của người dùng được ứng dụng thu thập
- password = PASSWORD – Mật khẩu của người dùng được thu thập bởi ứng dụng
- client_id = CLIENT_ID – ID ứng dụng khách nhận được khi bạn tạo ứng dụng lần đầu tiên
Máy chủ trả lời bằng mã thông báo truy cập có cùng định dạng với các grant type khác.
Lưu ý, client secret không được bao gồm ở đây theo giả định rằng hầu hết các trường hợp sử dụng để cấp mật khẩu sẽ là các ứng dụng dành cho thiết bị di động hoặc máy tính để bàn, nơi bí mật không thể được bảo vệ.
Truy cập ứng dụng
Trong một số trường hợp, ứng dụng có thể cần mã thông báo truy cập để có hành động cho chính nó chứ không phải người dùng.
Ví dụ: dịch vụ có thể cung cấp cách để ứng dụng cập nhật thông tin của riêng nó như URL hoặc biểu tượng trang web hoặc có thể muốn nhận thống kê về người dùng ứng dụng. Trong trường hợp này, các ứng dụng cần có cách để nhận mã thông báo truy cập cho tài khoản của riêng nó, bên ngoài ngữ cảnh của bất kỳ người dùng cụ thể nào. OAuth cung cấp loại cấp phép client_credentials cho mục đích này.
Để sử dụng thông tin đăng nhập của khách hàng, hãy tạo một yêu cầu POST như sau:
POST https://api.authorization-server.com/token
grant_type=client_credentials&
client_id=CLIENT_ID&
client_secret=CLIENT_SECRET
Phản hồi sẽ bao gồm mã thông báo truy cập có cùng định dạng với các loại cấp phép khác.
IV. Thực hiện yêu cầu được xác thực
Kết quả cuối cùng của tất cả các loại cấp phép là lấy mã thông báo truy cập.
Giờ bạn đã có mã thông báo truy cập, bạn có thể gửi yêu cầu tới API. Bạn có thể nhanh chóng thực hiện một yêu cầu API bằng cách sử dụng cURL như sau:
Ví dụ: curl -H “Authorization: Bearer RsT5OjbzRn430zqMLgV3Ia” \
https://api.authorization-server.com/1/me
Đảm bảo bạn luôn gửi yêu cầu qua HTTPS và không bao giờ bỏ qua chứng chỉ không hợp lệ. HTTPS là điều duy nhất bảo vệ các yêu cầu khỏi bị chặn hoặc sửa đổi.
V. Sự khác biệt từ OAuth 1.0
OAuth 1.0 phần lớn dựa trên các giao thức độc quyền hiện có như Flickr “FlickrAuth” và “AuthSub” của Google. Kết quả là giải pháp tốt nhất dựa trên kinh nghiệm thực hiện thực tế. Tuy nhiên, sau vài năm làm việc với giao thức, cộng đồng đã học đủ để suy nghĩ lại và cải thiện giao thức trong ba lĩnh vực chính nơi OAuth 1.0 tỏ ra bị giới hạn:
Xác thực và chữ ký
Phần lớn sự nhầm lẫn và phiền toái của nhà phát triển với OAuth 1.0 là do các yêu cầu mật mã về việc ký yêu cầu với ID khách hàng và bí mật. Việc mất khả năng sao chép và dán các ví dụ cURL dễ dàng khiến cho việc bắt đầu nhanh hơn rất nhiều.
OAuth 2 nhận ra khó khăn này và thay thế chữ ký bằng cách yêu cầu HTTPS cho tất cả các liên lạc giữa các trình duyệt, máy khách và API.
Trải nghiệm người dùng và luồng ủy quyền thay thế
OAuth bao gồm hai phần chính, nhận mã thông báo truy cập và sử dụng mã thông báo truy cập để thực hiện yêu cầu. OAuth 1.0 hoạt động tốt nhất cho trình duyệt web trên máy tính để bàn nhưng không cung cấp trải nghiệm người dùng tốt cho ứng dụng dành cho thiết bị di động và máy tính để bàn gốc hoặc các thiết bị thay thế như bảng điều khiển trò chơi hoặc TV.
OAuth 2 hỗ trợ trải nghiệm người dùng tốt hơn cho các ứng dụng gốc và hỗ trợ mở rộng giao thức để cung cấp khả năng tương thích với các yêu cầu thiết bị trong tương lai.
Hiệu suất quy mô
Khi các nhà cung cấp lớn hơn bắt đầu sử dụng OAuth 1.0, cộng đồng nhanh chóng nhận ra rằng giao thức không có quy mô tốt. Nhiều bước yêu cầu quản lý tình trạng và thông tin đăng nhập tạm thời, yêu cầu bộ nhớ được chia sẻ và khó đồng bộ hóa giữa các trung tâm dữ liệu. OAuth 1.0 cũng yêu cầu máy chủ API có quyền truy cập vào ID và bí mật của ứng dụng, thường phá vỡ kiến trúc của hầu hết các nhà cung cấp lớn, nơi máy chủ ủy quyền và máy chủ API hoàn toàn tách biệt.
OAuth 2 hỗ trợ việc tách vai trò của việc nhận ủy quyền người dùng và xử lý các cuộc gọi API. Các nhà cung cấp lớn hơn cần khả năng mở rộng này là miễn phí để thực hiện nó như vậy, và các nhà cung cấp nhỏ hơn có thể sử dụng cùng một máy chủ cho cả hai vai trò nếu họ muốn.
Dựa trên bài viết: