OAuth 2.0 定義了安全的授權流程,包括如何請求授權,以及透過授權結果請求 Access Token,但身分驗證(Authentication)的部分,並不在 OAuth 2.0 協定的範疇內。而 OpenID Connect 正是基於 OAuth2 的基礎上,再另外定義了身分驗證的流程,這能讓應用程式能夠依授權伺服器執行的身分驗證結果來確認使用者的身分,並且可以透過 API 取得使用者相關的資訊。
OpenID Connect 身為 OAuth 2.0 的擴充協定,大多數的原理或流程跟 OAuth 2.0 大多都相同。今天主要來介紹 OpenID Connect 跟 OAuth 2.0 不同的地方。
OpenID Connect 基於 OAuth 2.0 擴充,大多術語都跟 OAuth 2.0 很像,但其實有點不同,以下使用英文原名列表說明:
OAuth 2.0 的角色 | OpenID Connect 相似的角色 | OpenID Connect 說明 |
---|---|---|
Resource Owner | End-User | 最終使用者(簡稱使用者),OpenID Connect 協定指的是真實的使用者。而在 OAuth 2.0 授權的情境裡,有可能應用程式同時也是資源擁有者,這點略為不同。 |
Resource Server | UserInfo Endpoint | 提供使用者資訊的端點,OpenID Connect 主要是處理身分驗證,因此這個端點是提供使用者相關的資訊為主。OAuth 2.0 就包山包海,不侷限在身分驗證的場景上。 |
Client | Relying Party(RP) | 直譯是依賴方,但稱之應用程式(Application)不會有誤解,也比較好討論。基本上與 OAuth 2.0 相同。 |
Authorization Server | OpenID Provider(OP) | OpenID 服務提供者,提供 OpenID Connect 的服務,其實是指 Authorization Server 加上身分驗證功能的服務。 |
OAuth 2.0 的授權流程中,授權伺服器需要與使用者互動的流程(Authorization Code 與 Implicit)裡,是由應用程式發出授權請求(Authorization Request)由使用者同意授權,完成後再回給應用程式一個訊息,稱之為授權回應(Authorization Response)。
授權請求是一個 HTTP 請求,內容包含了應用程式唯一識別碼與相關資訊,還有請求授權的範圍。授權回應則是視授權類型決定內容,如 Authorization Code 會回應授權碼、Implicit 則是回應 Access Token。
在 OpenID Connect 的流程裡,一樣也有請求和回應,稱之為身分驗證請求(Authentication Request)和身分驗證回應(Authentication Response)。
身分驗證請求是基於授權請求再擴充出來的規格,OpenID Connect 定義了獨立的授權範圍 openid
,以及多個與身分驗證相關的自定義欄位。而身分驗證回應則多了一個 ID Token,內容是 JWT,包含了使用者身分相關聲明(cliam),包括主體(subject)的唯一識別碼。
參考文件
授權回應或是身分驗證回應,與 HTTP 回應(Response)是不同的。所以實務上,授權伺服器回應應用程式的方法,並不像 HTTP API 一樣有會有請求端點、方法或是回應狀態碼等。
此份協定定義了 Response Mode 作為回傳的方法,並使用新的參數 response_mode
指定。以下是定義的值,與實際的行為。
query
,參數編碼後,作為 redirect_uri
的 query 字串傳遞,通常用在 Authorization Code 授權類型。fragment
,參數編碼後,放在 redirect_uri
的 fragment 傳遞,通常用在 Implicit Grant 授權類型。form_post
,參數編碼後,透過授權回應一個自動提交表單的過場網頁,接著參數會放在表單的 input 裡,並透過 POST 傳到應用程式的 redirect_uri
。其中 form_post
的自動提交表單可以參考下面的範例:
<html>
<head><title>Submit This Form</title></head>
<body onload="javascript:document.forms[0].submit()">
<form method="post" action="https://client.example.org/callback">
<input type="hidden" name="state" value="DcP7csa3hMlvybERqcieLHrRzKBra"/>
<input type="hidden" name="id_token" value="eyJhbGciOiJSU... "/>
</form>
</body>
</html>
response_mode
實際的應用是,可以指定授權伺服器在回應的時候,要用什麼方法傳,當然要伺服器有支援才行。
參考文件
應用程式在發出授權請求的時候,可以要求授權伺服器最終回應類型(Response Type)為何。回應類型會直接對應到授權回應的內容或授權流程的形式。如:應用程式請求 response_type=code
的時候,授權伺服器會以 Authorization Code 的授權類型來執行授權流程,最終給應用程式的授權回應會是 code;而 response_type=token
的時候則會以 Implicit 授權類型來執行授權流程,最終給應用程式的授權回應則會是 Access Token。而這個協定在定義的是,如何將將這兩個摻在一起回應,如 response_type=code token
。
除此之外,協定還定義了兩個新的回應類型:id_token
是回傳 ID Token;none
則是什麼都沒有。除了 none
以外,其他三種 type 的組合技如下:
code token
code id_token
id_token token
code id_token token
以最後一個 code id_token token
例子來說,是指回授權到應用程式時,會把這三個參數一起帶回去。而組合的設計會衍生另一個問題是:順序要怎麼擺才對?類似地,scope
也有這種樣貌,但因為 OAuth 2.0 一開始定義的時候,就是空白隔開的多個授權範圍,所以沒有順序的問題,而 response_type
是定義成字串,才會衍生順序的問題。
這裡建議以協定的順序為主,尤其是要自己實作 OpenID Provider 的時候,更需要守規距。除非是實作 Client,且打算完全依賴 Discovery 的資訊(如 Google 的順序就跟協定的 code id_token token
不同)。
參考文件:
OpenID Connect 有額外定義一些身分驗證場景才會有的協定,如「從第三方啟動登入」這個協定就是授權場景沒有的;另外身分驗證相關的資訊其實是能夠定義得出來的,也就是 ID Token 的內容,如主體的唯一識別碼、身分驗證時間、Token 的發放與過期時間等;使用者的個人資訊也有額外定義聲明(Claims),如姓名、Email、生日等。
參考文件:
登入是使用者經過身分驗證完成,並保存已驗證狀態的一個過程,同時還會有另一個相對的行為是登出,是將已驗證狀態清除的過程。OpenID Connect 根據這個需求設計了狀態管理機制(Session Managerment)以及操作的方法。
另一個 Web 開發者所熟知的狀態管理機制--RFC 6265 - HTTP State Management Mechanism,也就是 Cookie 機制。Cookie 機制與 OpenID Connect 所定義的狀態管理不同的地方在於,Cookie 機制是依賴於網域,因此會有跨網域無法驗證的問題;而 OpenID Connect 要解決的問題就是跨網域的身分驗證和狀態管理,所以這也是兩種機制的主要差別。
額外一提:這四個規範已經在草稿階段很久了,在今天(9/18)突然發現它們都變最終規範了,發布時間是 9/12,這也是筆者第一個追規範從草稿追到定版的,覺得有點特別。