這是 ORY Hydra (下簡稱 Hydra)的登入與授權流程。了解它,才知道該如何使用 Hydra。
OAuth 2.0 與 Open ID Connect
Hydra 是一個 OAuth 2.0 Authorization 服務,同時也是 OpenID Connect 服務。有些人會誤以為這兩個服務的職責,是要保存使用者資料,並讓使用者登入。事實上,它們的任務是把使用者的憑據(credentials),轉換成 Access Token 或 OpenID Connect ID Token。這就有點像使用 cookie 儲存 session 資訊一樣,但它更加有彈性,也適合用在第三方應用程式。
Hydra 不存放使用者資料,如 profile、username、password。這些資料會被放在其他服務,而 Hydra 會使用 User Login and Consent Flow 的流程,來串接 Hydra 與這些服務。這個流程會使用 HTTP redirect 來將 authorization request 導去 Login Provider 與 Consent Provider。這兩個 provider 是可以自行實作的,可以是既有服務,或是新的應用程式。
概觀而言,這兩個服務的職責如下:
- Login Provider 要認證使用者,並驗證使用者輸入的帳號或密碼是否正確。
- Consent Provider 是使用者授權,它決定 OAuth 2.0 應用程式拿到的 Access Token 具有什麼樣的權限
另一個重要的觀念是有關 OAuth 2.0 Scope。
通常多數開發者,都會把 OAuth 2.0 Scope 與一般的存取控制(Access Control),像是 RBAC(Role Based Access Control)或 ACL(Access Control Lists)搞混。
在內部的存取控制是在限制使用者在系統能做什麼事。比方說系統管理員有所有的權限,而一般會員只能存取個人資訊。而相對的 OAuth 2.0 Scope 並不是在描述使用者能在系統做什麼事。
OAuth 2.0 Scope 在說的是使用者允許 Client 能代表使用者存取使用者的資源。比方說,使用者可以允許 Client 讀取使用者的照片,但不允許 Client 代表使用者上傳照片。所以,OAuth 2.0 Scope 表達的並不是使用者的權限。它表達的是 Client 可以代表使用者做哪些事。
以上是 Hydra 兩個最重要的部分概述。而 Hydra 主要功能是實作了 OAuth 2.0 與 OpenID Connect 的規範,以及 IETF OpenID Foundation 的規範。
下面將會介紹如何把現有的會員系統跟 Hydra 串接,這樣就能成為 OAuth 2.0 與 OpenID Connect 的 Provider,就像 Google、Facebook 一樣。
注意:以下章節建立在讀者已了解 OAuth 2.0 與 OpenID Connect 的前提下。
專有名詞
在開始講解細節前,先我們了解一些術語。這些術語其實就分散在 OAuth 2.0 或 OpenID Connect 協定裡。
這裡會使用更簡單的用詞來取代原本的用詞。
- resource owner 擁有資源者,一般指的就是使用者。
- OAuth 2.0 Authorization Server 是一個實作 OAuth 2.0 協定的服務,本文指的正是 Hydra。
- resource provider 是一個提供使用者資源的服務。
- OAuth 2.0 Client 是想存取使用者資源的服務。
- Identity Provider 是認證提供者,提供使用者的登入與註冊服務。也有可能會有管理介面可以管理使用者帳號與權限。
- User Agent,一般指的是瀏覽器。
- OpenID Connect 是一個建構在 OAuth 2.0 上的協定,主要是加上身分驗證的協定。
典型的 OAuth 2.0 流程如下:
- 開發者先在授權服務(Authorization Server,也就是 Hydra)上註冊 OAuth 2.0 Client,目的是要取得使用者資料。
- 授權服務會要求使用者授權 OAuth 2.0 Client 存取使用者資料。
- 使用者被轉導至授權服務。
- 授權服務確認使用者身分,並要求使用者授權 OAuth 2.0 Client 某些權限。
- 授權服務發行 token 給 OAuth 2.0 Client,以便可以代表使用者存取資源。
認證使用者與要求同意(consent)
前面已概述了 Hydra 的任務,這裡將會說明 User Login and Consent Flow 流程的細節。
這個流程會執行一連串的轉導,經過 Login Provider 認證使用者,再到 Consent Provider 授權。這兩個 Provider 都將由開發者自由實作。比方說用 NodeJS 實作可以接 /login 與 /consent 的 HTTP Server,這就能當作是 Login / Consent Provider 了。
一圖勝萬言,直接來看官方提供的循序圖:
細節如下:
- 首先 OAuth 2.0 Client 需要啟動一個 OpenID Connect 流程,實務上的做法是把使用者轉導到
http://hydra/oauth2/auth?client_id=...&...
。 - Hydra 如果發現使用者尚未驗證的話(也就是沒有 session 或 cookie 等),會把使用者轉導到 Login Provider。上面有提過,Login Provider 將由開發者自行實作,這裡就可以呈現登入介面給使用者,如帳密輸入之類的畫面。這時候的 URL 會類似像這樣:
http://login-service/login?login_challenge=1234...
。 - Login Provider 將會自己處理登入流程。登入成功後,需要把使用者的資訊(如 user ID)傳送給 Hydra。接著再轉導回 Hydra:
http://hydra/oauth2/auth?client_id=...&...&login_verifier=4321
。 - 回到 Hydra 後,Hydra 會再把使用者導去 Consent Provider,如:
http://consent-service/consent?consent_challenge=4567...
。 - Consent Provider 將會顯示要求使用者授權 OAuth 2.0 Client 哪些權限(也就是 OAuth 2.0 Scope)的介面。
- Consent Provider 在使用者完成授權時,會發出另一個請求給 Hydra,讓 Hydra 知道 Scope 有哪些。接著再轉導回 Hydra:
http://hydra/oauth2/auth?client_id=...&...&consent_verifier=7654...
。 - 接著再依 Hydra 的轉導,回到 OAuth 2.0 Client。
- 此時,使用者已完成認證與授權。最後,Client 即可跟 Hydra 要 Access、Refresh、ID Token。
這樣的設計允許開發者對登入系統,和授權頁的行為,能有更完整的控制(如,2FA)
實作 Login & Consent Provider
上面已經說明整個流程概觀,接著要說明如何實作這兩個 Provider。
OAuth 2.0 Authorize Code Flow
OAuth 2.0 Authorize Code Flow 是由 OAuth 2.0 Client 啟動流程的。通常是產生一個像這樣的 URL:
https://hydra/oauth2/auth?client_id=1234&scope=foo+bar&response_type=code&... |
然後再由 OAuth 2.0 Client 轉導使用者到這個 URL。
Hydra 處理授權 request
當使用者到了 Hydra 這個 URL,它會檢查是否有之前成功登入的 session cookie 記錄。除此之外,還會處理 id_token_hint
、prompt
、max_age
等相關參數。
接著,使用者會被導到 Login Provider。這個路徑將會採用 OAUTH2_LOGIN_URL
的環境設定。舉例來說,下面是設定與導頁的實際例子:
OAUTH2_LOGIN_URL=https://login-provider/login |
注意:不管這個使用者有沒有成功登入的 session cookie,或是否需要認證,Hydra 都會將使用者轉導至 Login Provider。
Login Provider
在處理 Hydra 過來的請求,如 https://login-provider/login?login_challenge=1234
時,首先要使用 login_challenge
的值,來呼叫 Hydra 所提供的 API 來取得身分驗證請求(authentication request)相關的資訊。
Laravel + Hydra SDK 程式碼範例如下:
public function login(Request $request, AdminApi $adminApi) |
$response
可以拿到的 JSON object,內容如下:
{ |
詳情可以查官方 API 文件
如果 skip 是 false
,就可以使用帳密表單,或是其他身分證明來提示使用者登入。
如果 skip 是 true
,則不應該顯示任何介面,而是使用呼叫 Hydra API 來接受登入請求。在這個步驟,也可以做更新使用者登入次數或相關記錄,甚至是自定義的商務邏輯。但一樣,不應該顯示任何介面。
接受請求的程式碼,可能會像下面這樣:
$body = [ |
下面這是拒絕登入的範例:
$body = [ |
使用者授權
到了這個階段,我們已經可以知道使用者是誰了,接著必須要問使用者,是否要授權這個 OAuth 2.0 Client 的授權請求。首先,會先檢查使用者過去是否有授權過。如果使用者從未授權,或是 OAuth 2.0 Client 要求過去未授予的權限,則必須要顯示畫面讓使用者授權。
這個過程類似登入流程,首先會被轉導至 Consent Provider。將會採用 OAUTH2_CONSENT_PROVIDER
的環境設定,下面是設定與導頁的實際例子:
OAUTH2_CONSENT_PROVIDER=https://consent-provider/consent |
與登入流程相同,無論是否有授權,或是有效的 session,都一定會轉導至 Consent Provider。
Consent Provider
Consent Provider 在處理 request 的時候,首先要使用 consent_challenge
跟 Hydra API 取得資訊。
下面是 Laravel + Hydra SDK 的程式碼範例:
public function consentPage(Request $request, AdminApi $adminApi) |
$response
將會有下面的資訊:
{ |
如果 skip
是 true
的話,不應該顯示使用者介面。取而代之的是,應該要接受(或拒絕)同意請求。一般來說,除非有充分的理由拒絕請求,不然應該要接受請求。
如果 skip
是 false
的話,則需要顯示授權頁,並使用 requested_scope
的資訊顯示使用者必須授權的權限列表。如果 OAuth 2.0 Client 是第一方的 Client,有些情境下會跳過這一步--因為這個 Client 跟認證系統是同一方的應用程式。
假設使用者同意請求,則跟登入流程類似:
$body = [ |
一樣可以拒絕授權,就跟登入流程一樣:
$body = [ |
若同意授權並轉導回 OAuth 2.0 Client 後,則 OAuth 2.0 的流程就到此結束。
撤銷 Consent and Login Session
Login
我們可以撤銷登入的 session 和 cookie,讓使用者在下一次啟動 OAuth 2.0 流程時,重新進行身分認證。使用 REST API 即可達成:
DELETE /oauth2/auth/sessions/login/{user}` |
注意:此做法將會刪除所有設備中的所有 cookie。
Consent
類似地,我們可以基於應用程式撤銷授權的 session 和 cookie,撤銷時,會把所有存取權限撤銷,並更新 token。
# 撤銷所有 session |
對應的文件為 Revokes all previous consent sessions of a user 與 Revokes consent sessions of a user for a specific OAuth 2.0 Client
OAuth 2.0 Scope
OAuth 2.0 Scope 定義了使用者授予 token 有什麼樣的權限。比方說,「可以存取公開資訊」、「可以上傳圖片」等等。這會在 Consent Provider 階段授權。
另外,Hydra 有預先定義了幾個 OAuth 2.0 Scope:
offline
和offline_access
:如果希望拿到 refresh token 的話,請加入這個 scope。openid
:如果希望執行 OpenID Connect 請求的話,請加入這個 scope。
OAuth2 Token Introspection
OAuth2 Token Introspection 是 IETF RFC 7662 標準。這是在定義 Resource Server 該用什麼樣的方法,來跟授權服務器確定 Token 的有效狀態。
Hydra 有提供 API POST /oauth2/introspect
,詳情可以翻閱文件;也有提供 CLI,執行方法如下:
hydra token introspect <token> |