[認證受權] 4.OIDC(OpenId Connect)身份認證受權(核心部分)

原文: [認證受權] 4.OIDC(OpenId Connect)身份認證受權(核心部分)

1 什麼是OIDC?

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

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.git

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

OIDC已經有不少的企業在使用,好比Google的帳號認證受權體系Microsoft的帳號體系也部署了OIDC,固然這些企業有的也是OIDC背後的推進者。除了這些以外,有不少各個語言版本的開源服務端組件,客戶端組件等等(http://openid.net/developers/certified/);web

理解OIDC的前提是須要理解OAuth2,這裏假設你們都有OAuth2的基礎,不清楚的能夠先閱讀本系列的前幾篇OAuth2的文章。json

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的接口,用戶獲取用戶的更完整的信息。session

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": "janedoe@example.com",
   "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

相關文章
相關標籤/搜索