OIDC(OpenId Connect)身份認證

1 什麼是OIDC?

看一下官方的介紹(http://openid.net/connect/):

OpenID Connect 1.0 is a simple identity layer on top of the OAuth 2.0 protocol. It allows Clients to verify the identity of the End-User based on the authentication performed by an Authorization Server, as well as to obtain basic profile information about the End-User in an interoperable and REST-like manner.

OpenID Connect allows clients of all types, including Web-based, mobile, and JavaScript clients, to request and receive information about authenticated sessions and end-users. The specification suite is extensible, allowing participants to use optional features such as encryption of identity data, discovery of OpenID Providers, and session management, when it makes sense for them.

簡單來說:OIDC是OpenID Connect的簡稱,OIDC=(Identity, Authentication) + OAuth 2.0。它在OAuth2上構建了一個身份層,是一個基於OAuth2協議的身份認證標準協議。我們都知道OAuth2是一個授權協議,它無法提供完善的身份認證功能(關於這一點請參考[認證授權] 3.基於OAuth2的認證(譯)),OIDC使用OAuth2的授權服務器來爲第三方客戶端提供用戶的身份認證,並把對應的身份認證信息傳遞給客戶端,且可以適用於各種類型的客戶端(比如服務端應用,移動APP,JS應用),且完全兼容OAuth2,也就是說你搭建了一個OIDC的服務後,也可以當作一個OAuth2的服務來用。應用場景如圖:

OIDC已經有很多的企業在使用,比如Google的賬號認證授權體系Microsoft的賬號體系也部署了OIDC,當然這些企業有的也是OIDC背後的推動者。除了這些之外,有很多各個語言版本的開源服務端組件,客戶端組件等等(http://openid.net/developers/certified/);

理解OIDC的前提是需要理解OAuth2,這裏假設大家都有OAuth2的基礎,不清楚的可以先閱讀本系列的前幾篇OAuth2的文章。

2 OIDC 協議族

OIDC本身是有多個規範構成,其中包含一個核心的規範,多個可選支持的規範來提供擴展支持,簡單的來看一下:

  1. Core:必選。定義OIDC的核心功能,在OAuth 2.0之上構建身份認證,以及如何使用Claims來傳遞用戶的信息。
  2. Discovery:可選。發現服務,使客戶端可以動態的獲取OIDC服務相關的元數據描述信息(比如支持那些規範,接口地址是什麼等等)。
  3. Dynamic Registration :可選。動態註冊服務,使客戶端可以動態的註冊到OIDC的OP(這個縮寫後面會解釋)。
  4. OAuth 2.0 Multiple Response Types :可選。針對OAuth2的擴展,提供幾個新的response_type。
  5. OAuth 2.0 Form Post Response Mode:可選。針對OAuth2的擴展,OAuth2回傳信息給客戶端是通過URL的querystring和fragment這兩種方式,這個擴展標準提供了一基於form表單的形式把數據post給客戶端的機制。
  6. Session Management :可選。Session管理,用於規範OIDC服務如何管理Session信息。
  7. Front-Channel Logout:可選。基於前端的註銷機制,使得RP(這個縮寫後面會解釋)可以不使用OP的iframe來退出。
  8. Back-Channel Logout:可選。基於後端的註銷機制,定義了RP和OP直接如何通信來完成註銷。

除了上面這8個之外,還有其他的正在制定中的擴展。看起來是挺多的,不要被嚇到,其實並不是很複雜,除了Core核心規範內容多一點之外,另外7個都是很簡單且簡短的規範,另外Core是基於OAuth2的,也就是說其中很多東西在複用OAuth2,所以說你理解了OAuth2之後,OIDC就是非常容易理解的了,我們這裏就只關注OIDC引入了哪些新的東西(Core,其餘7個可選規範不做介紹,但是可能會提及到)。千言萬語都不如一張圖,沒圖你說個***。

上圖是官方給出的一個OIDC組成結構圖,我們暫時只關注Core的部分,其他的部分了解是什麼東西就可以了,當作黑盒來用。就像當初的AJAX一樣,它其實並不是一個新的技術,而是結合很多已有的技術,按照規範的方式組合起來,就是AJAX。同理,OIDC也不是新技術,它主要是借鑑OpenId的身份標識,OAuth2的授權和JWT包裝數據的方式,把這些技術融合在一起就是OIDC。

3 OIDC 核心概念

OAuth2提供了Access Token來解決授權第三方客戶端訪問受保護資源的問題;OIDC在這個基礎上提供了ID Token來解決第三方客戶端標識用戶身份認證的問題。OIDC的核心在於在OAuth2的授權流程中,一併提供用戶的身份認證信息(ID Token)給到第三方客戶端,ID Token使用JWT格式來包裝,得益於JWT(JSON Web Token)的自包含性,緊湊性以及防篡改機制,使得ID Token可以安全的傳遞給第三方客戶端程序並且容易被驗證。此外還提供了UserInfo的接口,用戶獲取用戶的更完整的信息。

3.1 OIDC 主要術語

主要的術語以及概念介紹(完整術語參見http://openid.net/specs/openid-connect-core-1_0.html#Terminology):

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

3.2 OIDC 工作流程

從抽象的角度來看,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。

上圖取自Core規範文檔,其中AuthN=Authentication,表示認證;AuthZ=Authorization,代表授權。注意這裏面RP發往OP的請求,是屬於Authentication類型的請求,雖然在OIDC中是複用OAuth2的Authorization請求通道,但是用途是不一樣的,且OIDC的AuthN請求中scope參數必須要有一個值爲的openid的參數(後面會詳細介紹AuthN請求所需的參數),用來區分這是一個OIDC的Authentication請求,而不是OAuth2的Authorization請求。

3.3 ID Token

上面提到過OIDC對OAuth2最主要的擴展就是提供了ID Token。ID Token是一個安全令牌,是一個授權服務器提供的包含用戶信息(由一組Cliams構成以及其他輔助的Cliams)的JWT格式的數據結構。ID Token的主要構成部分如下(使用OAuth2流程的OIDC)。

  1. iss = Issuer Identifier:必須。提供認證信息者的唯一標識。一般是一個https的url(不包含querystring和fragment部分)。
  2. sub = Subject Identifier:必須。iss提供的EU的標識,在iss範圍內唯一。它會被RP用來標識唯一的用戶。最長爲255個ASCII個字符。
  3. aud = Audience(s):必須。標識ID Token的受衆。必須包含OAuth2的client_id。
  4. exp = Expiration time:必須。過期時間,超過此時間的ID Token會作廢不再被驗證通過。
  5. iat = Issued At Time:必須。JWT的構建的時間。
  6. auth_time = AuthenticationTime:EU完成認證的時間。如果RP發送AuthN請求的時候攜帶max_age的參數,則此Claim是必須的。
  7. nonce:RP發送請求的時候提供的隨機字符串,用來減緩重放攻擊,也可以來關聯ID Token和RP本身的Session信息。
  8. acr = Authentication Context Class Reference:可選。表示一個認證上下文引用值,可以用來標識認證上下文類。
  9. amr = Authentication Methods References:可選。表示一組認證方法。
  10. azp = Authorized party:可選。結合aud使用。只有在被認證的一方和受衆(aud)不一致時才使用此值,一般情況下很少使用。

ID Token通常情況下還會包含其他的Claims(畢竟上述claim中只有sub是和EU相關的,這在一般情況下是不夠的,必須還需要EU的用戶名,頭像等其他的資料,OIDC提供了一組公共的cliams,請移步這裏http://openid.net/specs/openid-connect-core-1_0.html#StandardClaims)。另外ID Token必須使用JWS進行簽名和JWE加密,從而提供認證的完整性、不可否認性以及可選的保密性。一個ID Token的例子如下:

 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   }

3.4 認證

解釋完了ID Token是什麼,下面就看一下OIDC如何獲取到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。

這裏有個小問題大家可以思考下,OAuth2中還有基於Resource Owner Password Credentials Grant和Client Credentials Grant的方式來獲取Access Token,爲什麼OIDC沒有擴展這些方式呢?

Resource Owner Password Credentials Grant是需要用途提供賬號密碼給RP的,賬號密碼給到RP了,還要什麼自行車(ID Token)。。。

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

3.4.1 基於Authorization Code的認證請求

這種方式使用OAuth2的Authorization Code的方式來完成用戶身份認證,所有的Token都是通過Token EndPoint(OAuth2中定義:https://tools.ietf.org/html/rfc6749#section-3.2)來發放的。構建一個OIDC的Authentication Request需要提供如下的參數:

  1. scope:必須。OIDC的請求必須包含值爲“openid”的scope的參數。
  2. response_type:必選。同OAuth2。
  3. client_id:必選。同OAuth2。
  4. redirect_uri:必選。同OAuth2。
  5. state:推薦。同OAuth2。防止CSRF, XSRF。

以上這5個參數是和OAuth2相同的。除此之外,還定義瞭如下的參數:

  1. response_mode:可選。OIDC新定義的參數(OAuth 2.0 Form Post Response Mode),用來指定Authorization Endpoint以何種方式返回數據。
  2. nonce:可選。ID Token中的出現的nonce就是來源於此。
  3. display : 可選。指示授權服務器呈現怎樣的界面給EU。有效值有(page,popup,touch,wap),其中默認是page。page=普通的頁面,popup=彈出框,touch=支持觸控的頁面,wap=移動端頁面。
  4. prompt:可選。這個參數允許傳遞多個值,使用空格分隔。用來指示授權服務器是否引導EU重新認證和同意授權(consent,就是EU完成身份認證後的確認同意授權的頁面)。有效值有(none,login,consent,select_account)。none=不實現現任何認證和確認同意授權的頁面,如果沒有認證授權過,則返回錯誤login_required或interaction_required。login=重新引導EU進行身份認證,即使已經登錄。consent=重新引導EU確認同意授權。select_account=假如EU在授權服務器有多個賬號的話,允許EU選擇一個賬號進行認證。
  5. max_age:可選。代表EU認證信息的有效時間,對應ID Token中auth_time的claim。比如設定是20分鐘,則超過了時間,則需要引導EU重新認證。
  6. ui_locales:可選。用戶界面的本地化語言設置項。
  7. id_token_hint:可選。之前發放的ID Token,如果ID Token經過驗證且是有效的,則需要返回一個正常的響應;如果有誤,則返回對應的錯誤提示。
  8. login_hint:可選。向授權服務器提示登錄標識符,EU可能會使用它登錄(如果需要的話)。比如指定使用用戶使用blackheart賬號登錄,當然EU也可以使用其他賬號登錄,這只是類似html中input元素的placeholder。
  9. acr_values:可選。Authentication Context Class Reference values,對應ID Token中的acr的Claim。此參數允許多個值出現,使用空格分割。

以上是基於Authorization Code方式的OIDC的認證請求所需的參數。在OIDC的其他認證流程中也會有其他的參數或不同的參數值(稍有差異)。一個簡單的示例如下:

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

也可以是一個基於302的重定向方式。

3.4.2 基於Authorization Code的認證請求的響應

在授權服務器接收到認證請求之後,需要對請求參數做嚴格的驗證,具體的規則參見http://openid.net/specs/openid-connect-core-1_0.html#AuthRequestValidation,驗證通過後引導EU進行身份認證並且同意授權。在這一切都完成後,會重定向到RP指定的回調地址,並且把code和state參數傳遞過去。比如:

  HTTP/1.1 302 Found
  Location: https://client.example.org/cb?
    code=SplxlOBeZQQYbYS6WxSbIA
    &state=af0ifjsldkj

3.4.3 獲取ID Token

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

  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"
  }

其中看起來一堆亂碼的部分就是JWT格式的ID Token。在RP拿到這些信息之後,需要對id_token以及access_token進行驗證(具體的規則參見http://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidationhttp://openid.net/specs/openid-connect-core-1_0.html#ImplicitTokenValidation)。至此,可以說用戶身份認證就可以完成了,後續可以根據UserInfo EndPoint獲取更完整的信息。

3.4.4 Implicit Flow和Hybrid Flow

Implicit Flow的工作方式是在OAuth2 Implicit Flow上附加提供id_token,當然,認證請求的參數和基於Authorization Code的流程稍有不同,具體的差異參見http://openid.net/specs/openid-connect-core-1_0.html#ImplicitAuthRequest,這裏就不做詳細介紹了。

Hybrid Flow則=Authorization Code Flow+Implicit Flow,也不再詳細介紹了。

3.5 UserInfo Endpoint

UserIndo EndPoint是一個受OAuth2保護的資源。在RP得到Access Token後可以請求此資源,然後獲得一組EU相關的Claims,這些信息可以說是ID Token的擴展,比如如果你覺得ID Token中只需包含EU的唯一標識sub即可(避免ID Token過於龐大),然後通過此接口獲取完整的EU的信息。此資源必須部署在TLS之上,例如:

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

成功之後響應如下:

  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"
  }

其中sub代表EU的唯一標識,這個claim是必須的,其他的都是可選的。

4 總結

繼OAuth2之後,感覺OIDC也要大放異彩了。其本身是一個完全開放的標準,而且兼容衆多的已有的IDP(身份提供商),比如基於SAML的、基於WS-Federation的等等已有的身份認證系統,都可以作爲OIDC的OP存在。總結一下OIDC有那些特性和好處吧:

  1. OIDC使得身份認證可以作爲一個服務存在。
  2. OIDC可以很方便的實現SSO(跨頂級域)。
  3. OIDC兼容OAuth2,可以使用Access Token控制受保護的API資源。
  4. OIDC可以兼容衆多的IDP作爲OIDC的OP來使用。
  5. OIDC的一些敏感接口均強制要求TLS,除此之外,得益於JWT,JWS,JWE家族的安全機制,使得一些敏感信息可以進行數字簽名、加密和驗證,進一步確保整個認證過程中的安全保障。

以上內容均是個人的一些理解,如果錯誤之處,歡迎指正!

5 Example

筆者基於IdentityServer3和IdentitySever4(兩者都是基於OIDC的一個.NET版本的開源實現)寫的一個集成SSO,API訪問授權控制,QQ聯合登陸(作爲OP)的demo:https://github.com/linianhui/oidc.example 。

6 參考

官方資料:

http://openid.net/connect/

http://openid.net/connect/faq/

http://openid.net/developers/certified/

JWT : https://tools.ietf.org/html/rfc7519

JWS:https://tools.ietf.org/html/rfc7515

JWE:https://tools.ietf.org/html/rfc7516

.NET的開源實現:https://github.com/IdentityServer

視頻:Identity, Authentication + OAuth = OpenID Connect

案例:

https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-protocols-openid-connect-code

https://developers.google.com/identity/protocols/OpenIDConnect

 

原文地址:https://www.cnblogs.com/linianhui/p/openid-connect-core.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章