基於OAuth2的OIDC (OpenId Connect)身份認證

OIDC協議

OIDC(OpenID Connect)是在OAuth2上構建了一個身份層,是一個基於OAuth2協議的身份認證標準協議。

OAuth2協議

OAuth2是一個授權協議,它無法提供完善的身份認證功能【1】,OIDC使用OAuth2的授權服務器來為第三方客戶端提供用戶的身份認證,並把對應的身份認證信息傳遞給客戶端。

使用OAuth2進行認證的常見誤區

如果用OAuth2進行認證,會有許多問題。

  • 在OAuth2中,token被設計成是對客戶端不透明的,但在用戶身份認證的上下文環境中, 客戶端往往需要能夠從token中派生一些信息。這可以通過定義一個雙重目的(dual-purposing)的客戶端以解析和理解的token來完成。但是OAuth2協議並沒有為access token本身定義特定的格式貨結構,因此需要在OAuth2協議的基礎上,利用諸如OpenId Connect的ID Token在響應中提供一個次要的標記,它將和access token一起發送給客戶端中。一方面,保持了token是對客戶端的不透明,另一方面,為客戶端提供了其所需的認證信息。
  • OAuth保護的是資源,獲取用戶屬性的API(identity API)通常沒有辦法告訴你用戶是否存在。而且,在某些情況下,用戶無需身份驗證即可獲得access token(比如[認證授權] 1.OAuth2授權 - 5.4 Client Credentials Grant)。也就是說Access Token並不能和某個用戶關聯起來。
  • 在某些場合,例如,使用implicit流程(這個流程中直接把acces token作為url的hash參數([認證授權] 1.OAuth2 授權 - 5.2.2 Access Token Response))中,User Agent可以獲得token,也就你開闢了一個注入 access token到應用程序外部(並可能在應用程序外部洩露)的地方。如果Client不通過某種機制驗證access token,則它無法區分access token是有效的令牌還是攻擊的令牌。
  • 很可能有一個幼稚的(naive)客戶端,從其他的客戶端拿到一個有效的token來作為自己的登錄事件。畢竟token是有效的,對API的訪問也會返回有效的用戶信息。問題在於沒有用戶做任何事情來證明用戶存在,在這種情況下,用戶甚至都沒有授權給幼稚的(naive)客戶端。
  • 如果攻擊者能夠攔截或者替換來自Client的一個調用,它可能會改變返回的用戶信息,而客戶端卻無法感知這一情況。這將允許攻擊者通過簡單地在正確的調用序列中交換用戶標識符來模擬一個幼稚的(naive)Client上的用戶。
  • 基於OAuth 身份(identity)API的最大問題在於,即使使用完全符合OAuth的機制,不同的提供程序不可避免的會使用不同的方式實現身份(identity)API。

OIDC核心概念:ID Token

OAuth2提供了Access Token來解決授權第三方客戶端訪問受保護資源的問題;OIDC在這個基礎上提供了ID Token來解決第三方客戶端標識用戶身份認證的問題。OIDC的核心在於在OAuth2的授權流程中,一併提供用戶的身份認證信息(ID Token)給到第三方客戶端,ID Token使用JWT格式來包裝。此外還提供了UserInfo的接口,用戶獲取用戶的更完整的信息。

參與角色

主要的術語以及概念介紹:

  • EU:End User:一個人類用戶。
  • RP:Relying Party ,用來代指OAuth2中的受信任的客戶端,身份認證和授權信息的消費方;
  • OP:OpenID Provider,有能力提供EU認證的服務(比如OAuth2中的授權服務),用來為RP提供EU的身份認證信息;
  • ID Token:JWT格式的數據,包含EU身份認證的信息。
  • UserInfo Endpoint:用戶信息接口(受OAuth2保護),當RP使用Access Token訪問時,返回授權用戶的信息,此接口必須使用HTTPS。

工作流程

OIDC的流程由以下5個步驟構成:

  1. RP發送一個認證請求給OP;
  2. OP對EU進行身份認證,然後提供授權;
  3. OP把ID Token和Access Token(需要的話)返回給RP;
  4. RP使用Access Token發送一個請求UserInfo EndPoint;
  5. UserInfo EndPoint返回EU的Claims。
    oidc.jpg
    注意這裡面RP發往OP的請求,是屬於Authentication類型的請求,雖然在OIDC中是複用OAuth2的Authorization請求通道,但是用途是不一樣的,且OIDC的AuthN請求中scope參數必須要有一個值為的openid的參數,用來區分這是一個OIDC的Authentication請求,而不是OAuth2的Authorization請求。

什麼是ID Token

OIDC對OAuth2最主要的擴展就是提供了ID Token。ID Token是一個安全令牌,是一個授權服務器提供的包含用戶信息(由一組Cliams構成以及其他輔助的Cliams)的JWT格式的數據結構。 另外ID Token必須使用JWS進行簽名和JWE加密,從而提供認證的完整性、不可否認性以及可選的保密性。一個ID Token的例子如下:

<code> 1 {
2 "iss": "https://server.example.com",
3 "sub": "24400320",
4 "aud": "s6BhdRkqt3",
5 "nonce": "n-0S6_WzA2Mj",
6 "exp": 1311281970,
7 "iat": 1311280970,
8 "auth_time": 1311280969,
9 "acr": "urn:mace:incommon:iap:silver"
10 }
/<code>

如何獲取ID Token

因為OIDC基於OAuth2,所以OIDC的認證流程主要是由OAuth2的幾種授權流程延伸而來的,有以下3種:

  1. Authorization Code Flow:使用OAuth2的授權碼來換取Id Token和Access Token。
  2. Implicit Flow:使用OAuth2的Implicit流程獲取Id Token和Access Token。
  3. Hybrid Flow:混合Authorization Code Flow+Implici Flow。

Resource Owner Password Credentials Grant是需要用途提供賬號密碼給RP的,賬號密碼給到RP了,就不再需要ID Token了。

Client Credentials Grant這種方式根本就不需要用戶參與,更談不上用戶身份認證了。這也能反映授權和認證的差異,以及只使用OAuth2來做身份認證的事情是遠遠不夠的,也是不合適的。

這裡主要介紹基於Authorization Code的認證請求/答覆。

基於Authorization Code的認證請求的請求

所有的Token都是通過Token EndPoint來發放的。構建一個OIDC的Authentication Request需要提供如下的參數:

  • scope:必須。OIDC的請求必須包含值為openid的scope的參數。
  • response_type:必選。同OAuth2。
  • client_id:必選。同OAuth2。
  • redirect_uri:必選。同OAuth2。
  • state:推薦。同OAuth2。防止CSRF, XSRF。

以上這5個參數是和OAuth2相同的。除此之外,還定義了一些參數【2】例如:

  • response_mode:可選。OIDC新定義的參數,用來指定Authorization Endpoint以何種方式返回數據。

一個簡單的示例如下:

<code>GET /authorize?
response_type=code
&scope=openid%20profile%20email
&client_id=s6BhdRkqt3
&state=af0ifjsldkj
&redirect_uri=https%3A%2F%2Fclient.example.org%2Fcb HTTP/1.1
Host: server.example.com
/<code>
基於Authorization Code的認證請求的響應

在授權服務器接收到認證請求之後,需要對請求參數做嚴格的驗證。驗證通過後引導EU進行身份認證並且同意授權。在這一切都完成後,會重定向到RP指定的回調地址,並且把code和state參數傳遞過去。比如:

<code>  HTTP/1.1 302 Found
Location: https://client.example.org/cb?
code=SplxlOBeZQQYbYS6WxSbIA
&state=af0ifjsldkj
/<code>
獲取ID Token

RP使用上一步獲得的code來請求Token EndPoint,這一步同OAuth2。然後Token EndPoint會返回響應的Token,其中除了OAuth2規定的部分數據外,還會附加一個id_token的字段。id_token字段就是上面提到的ID Token。例如:

<code>  HTTP/1.1 200 OK 

Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache

{
"access_token": "SlAV32hkKG",
"token_type": "Bearer",
"refresh_token": "8xLOxBtZp8",
"expires_in": 3600,
"id_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjFlOWdkazcifQ.ewogImlzc
yI6ICJodHRwOi8vc2VydmVyLmV4YW1wbGUuY29tIiwKICJzdWIiOiAiMjQ4Mjg5
NzYxMDAxIiwKICJhdWQiOiAiczZCaGRSa3F0MyIsCiAibm9uY2UiOiAibi0wUzZ
fV3pBMk1qIiwKICJleHAiOiAxMzExMjgxOTcwLAogImlhdCI6IDEzMTEyODA5Nz
AKfQ.ggW8hZ1EuVLuxNuuIJKX_V8a_OMXzR0EHR9R6jgdqrOOF4daGU96Sr_P6q
Jp6IcmD3HP99Obi1PRs-cwh3LO-p146waJ8IhehcwL7F09JdijmBqkvPeB2T9CJ
NqeGpe-gccMg4vfKjkM8FcGvnzZUN4_KSP0aAp1tOJ1zZwgjxqGByKHiOtX7Tpd
QyHE5lcMiKPXfEIQILVq0pc_E2DzL7emopWoaoZTF_m0_N0YzFC6g6EJbOEoRoS
K5hoDalrcvRYLSrQAZZKflyuVCyixEoV9GfNQC3_osjzw2PAithfubEEBLuVVk4
XUVrWOLrLl0nx7RkKU8NXNHq-rvKMzqg"
}
/<code>

其中看起來一堆亂碼的部分就是JWT格式的ID Token。在RP拿到這些信息之後,需要對id_token以及access_token進行驗證。至此,可以說用戶身份認證就可以完成了,後續可以根據UserInfo EndPoint獲取更完整的信息。

OIDC協議簇

除了Core核心規範內容多一點之外,另外7個都是很簡單且簡短的規範,另外Core是基於OAuth2的,也就是說其中很多東西在複用OAuth2。


基於OAuth2的OIDC (OpenId Connect)身份認證


OpenId Connect定義了一個發現協議,它允許Client輕鬆的獲取有關如何和特定的身份認證提供者進行交互的信息。在另一方面,還定義了一個Client註冊協議,允許Client引入新的身份提供程序(identity providers)。通過這兩種機制和一個通用的身份API,OpenId Connect可以運行在互聯網規模上運行良好,在那裡沒有任何一方事先知道對方的存在。

UserInfo Endpoint

UserIndo EndPoint是一個受OAuth2保護的資源。在RP得到Access Token後可以請求此資源,然後獲得一組EU相關的Claims,這些信息可以說是ID Token的擴展,

<code>  GET /userinfo HTTP/1.1
Host: server.example.com
Authorization: Bearer SlAV32hkKG
/<code>

成功之後相應如下:

<code>  HTTP/1.1 200 OK
Content-Type: application/json

{
"sub": "248289761001",
"name": "Jane Doe",
"given_name": "Jane",
"family_name": "Doe",
"preferred_username": "j.doe",
"email": "[email protected]",
"picture": "http://example.com/janedoe/me.jpg"
}
/<code>


作者:趙陽_c149
鏈接:https://www.jianshu.com/p/dacf05f6fa83
來源:簡書
著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。


分享到:


相關文章: