認證授權:學習OIDC

cwsheng發表於2020-08-30

 

前言

  上一篇文章介紹了OAuth2.0協議的相關內容,知道OAuth2.0是一個授權協議,無法提供完善的身份認證功能。那麼什麼來解決身份認證功能呢?——OIDC是一個不錯的解決方案。接下來進一步來了解OIDC是什麼,有什麼效果呢?

一、OIDC是什麼

  OIDC是OpenID Connect 的簡稱,以下是OIDC官方的描述內容:

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是基於OAuth2.0協議的身份層,允許客戶端基於授權伺服器執行的身份驗證來驗證終端使用者的身份,並以可互操作且類似於REST的方式獲取有關最終​​使用者的基本配置檔案資訊。並且支援所有客戶端型別(如基於Web的客戶端,移動客戶端和JavaScript客戶端)請求並接收有關經過身份驗證的會話和終端使用者的資訊

二、OIDC相關的協議

  OIDC本身是有多個規範構成,其中最主要的是一個核心的規範,多個可選支援的規範來提供擴充套件支援

   主要包含:

   Core:必選。定義OIDC的核心功能,在OAuth 2.0之上構建身份認證,以及如何使用Claims來傳遞使用者的資訊。
   Discovery:可選。發現服務,使客戶端可以動態的獲取OIDC服務相關的後設資料描述資訊
   Dynamic Client Registration:可選。動態註冊服務,使客戶端可以動態的註冊到OIDC的OP
   Session Management:可選。Session管理,用於規範OIDC服務如何管理Session資訊
   Form Post Response Mode:可選。針對OAuth2的擴充套件,OAuth2回傳資訊給客戶端是通過URL的querystring和fragment這兩種方式,這個擴充套件標準提供了一基於form表單的形式把資料post給客戶端的機制。
   基礎協議:
    OAuth2.0 Core:https://tools.ietf.org/html/rfc6749
    OAuth2.0 Bearer:https://tools.ietf.org/html/rfc6750
    OAuth2.0 Assertions:https://tools.ietf.org/html/rfc7521
    OAuth2.0 JWT Profile:https://tools.ietf.org/html/rfc7523
    OAuth2.0 Responses:可選。針對OAuth2的擴充套件,提供幾個新的response_type。
    JWT(JSON Web Token):https://tools.ietf.org/html/rfc7519
    JWS(JSON Web Signature):https://tools.ietf.org/html/rfc7515
    JWE(JSON Web Encryption):https://tools.ietf.org/html/rfc7516
    JWK(JSON Web Key):https://tools.ietf.org/html/rfc7517
    JWA(JSON Web Algorithms):https://tools.ietf.org/html/rfc7518
    WebFinger:https://tools.ietf.org/html/rfc7033

  

 

  上圖是官方給出的一個OIDC組成結構圖,可以看出OIDC不是什麼新技術,它主要是借鑑OpenId的身份標識,OAuth2的授權和JWT包裝資料的方式,組合使用這些技術就是現在的OIDC。

  接下來就來學習裡面最核心的相關內容(Core核心規範)。

 三、OIDC核心規範

  1、主要術語

    主要的術語以及概念介紹

    • 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。

  2、主要流程

    抽象的看主要包含以下步驟: 

    1. RP(客戶端)將請求傳送到OpenID提供程式(OP)。
    2. OP驗證終端使用者並獲得授權。
    3. OP用ID Token(通常是訪問令牌)進行響應。
    4. RP可以將帶有訪問令牌(Access Token)的請求傳送到UserInfo EndPoint。
    5. UserInfo端點返回有關終端使用者(EU)的宣告(Claims)。 

    

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

   3、ID Token

    ID Token是OpenID Connect對OAuth 2.0進行的主要擴充套件(用於使終端使用者能夠通過身份驗證),ID Token是一種安全令牌,其中包含有關使用客戶端時授權伺服器對終端使用者的身份驗證的宣告(Claims)以及可能的其他請求的宣告(Claims)的JWT格式資料。

    ID Token的主要構成部分如下:

    

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

   一般情況在ID Token中還會包含其他宣告(Claims)內容,如使用者姓名、頭像等資訊。OIDC提供了一組公共的cliams,可以到這裡檢視。另外ID Token必須使用JWS進行簽名和JWE加密,從而提供認證的完整性、不可否認性以及可選的保密性。

   例如:

  {
   "iss": "https://server.example.com",
   "sub": "24400320",
   "aud": "s6BhdRkqt3",
   "nonce": "n-0S6_WzA2Mj",
   "exp": 1311281970,
   "iat": 1311280970,
   "auth_time": 1311280969,
   "acr": "urn:mace:incommon:iap:silver"
  }

  4、認證

   瞭解了ID Token後,接下來就來看OIDC如何獲取ID Token的,由於OIDC基於OAuth2,所以OIDC的認證流程主要是由OAuth2的幾種授權流程延伸而來的,

   有以下3種

    • Authorization Code Flow:使用OAuth2的授權碼來換取Id Token和Access Token。
    • Implicit Flow:使用OAuth2的Implicit流程獲取Id Token和Access Token。
    • 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來做身份認證的事情是遠遠不夠的,也是不合適的。

   4.1 授權碼流程(Authorization Code Flow)

授權碼流程執行以下步驟。 

    1. 客戶端準備一個包含所需請求引數的身份驗證請求。
    2. 客戶端將請求傳送到授權伺服器。
    3. 授權伺服器對終端使用者進行身份驗證。
    4. 授權伺服器獲得終端使用者同意/授權。
    5. 授權伺服器使用授權碼將終端使用者傳送回客戶端。
    6. 客戶端使用令牌端點上的授權碼來請求響應。
    7. 客戶端收到響應,該響應在響應主體中包含ID令牌和訪問令牌。
    8. 客戶端驗證ID令牌並檢索終端使用者的主題識別符號。

    4.1.1 認證請求

      該方式使用OAuth2的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
response_mode OIDC新定義的引數(OAuth 2.0 Form Post Response Mode),用來指定Authorization Endpoint以何種方式返回資料
nonce ID Token中的出現的nonce就是來源於此
display 指示授權伺服器呈現怎樣的介面給EU。有效值有(page,popup,touch,wap),其中預設是page。page=普通的頁面,popup=彈出框,touch=支援觸控的頁面,wap=移動端頁面。
prompt 這個引數允許傳遞多個值,使用空格分隔。用來指示授權伺服器是否引導EU重新認證和同意授權(consent,就是EU完成身份認證後的確認同意授權的頁面)。有效值有(none,login,consent,select_account)。none=不實現現任何認證和確認同意授權的頁面,如果沒有認證授權過,則返回錯誤login_required或interaction_required。login=重新引導EU進行身份認證,即使已經登入。consent=重新引導EU確認同意授權。select_account=假如EU在授權伺服器有多個賬號的話,允許EU選擇一個賬號進行認證。
max_age 代表EU認證資訊的有效時間,對應ID Token中auth_time的claim。比如設定是20分鐘,則超過了時間,則需要引導EU重新認證
ui_locales 使用者介面的本地化語言設定項
id_token_hint 之前發放的ID Token,如果ID Token經過驗證且是有效的,則需要返回一個正常的響應;如果有誤,則返回對應的錯誤提示
login_hint 向授權伺服器提示登入識別符號,EU可能會使用它登入(如果需要的話)。比如指定使用使用者使用blackheart賬號登入,當然EU也可以使用其他賬號登入,這只是類似html中input元素的placeholder。
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

    4.1.2 認證請求的響應

      授權伺服器接收到請求後,進行了:OAuth2.0引數驗證、scope引數是否包含openid的範圍值、授權伺服器必須引數是否存等相關驗證(詳見);驗證通過後引導EU進行身份認證並同意授權。然後會重定向到RP指定的回撥地址,並且把code和state引數傳遞過去:如下:

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

     4.1.3 獲取IDToken

      接下來PR會根據獲取的Code來請求Token EndPoint,和OAuth2.0請求相同;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"
  }

  4.2 隱式流(Implicit Flow)

隱式流遵循以下步驟: 

    1. 客戶端準備一個包含所需請求引數的身份驗證請求。
    2. 客戶端將請求傳送到授權伺服器。
    3. 授權伺服器對終端使用者進行身份驗證。
    4. 授權伺服器獲得終端使用者同意/授權。
    5. 授權伺服器使用ID令牌和訪問令牌(如果要求)將終端使用者傳送回客戶端。
    6. 客戶端驗證ID令牌並檢索終端使用者的主題識別符號。   

   4.2.1 認證請求

     按照4.1.1定義進行身份驗證請求,不同的是,這些身份驗證請求引數的使用方式如下:

    • response_type值換成id_token token 或 id_token

     示例如下:      

  GET /authorize?
    response_type=id_token%20token
    &client_id=s6BhdRkqt3
    &redirect_uri=https%3A%2F%2Fclient.example.org%2Fcb
    &scope=openid%20profile
    &state=af0ifjsldkj
    &nonce=n-0S6_WzA2Mj HTTP/1.1
  Host: server.example.com

   4.2.1 認證請求響應

     響應結果如下:

  HTTP/1.1 302 Found
  Location: https://client.example.org/cb#
    access_token=SlAV32hkKG
    &token_type=bearer
    &id_token=eyJ0 ... NiJ9.eyJ1c ... I6IjIifX0.DeWt4Qu ... ZXso
    &expires_in=3600
    &state=af0ifjsldkj

  4.3 混合流(Hybrid Flow)

混合流遵循以下步驟: 

    1. 客戶端準備一個包含所需請求引數的身份驗證請求。
    2. 客戶端將請求傳送到授權伺服器。
    3. 授權伺服器對終端使用者進行身份驗證。
    4. 授權伺服器獲得終端使用者同意/授權。
    5. 授權伺服器使用授權碼以及一個或多個其他引數(根據響應型別)將終端使用者傳送回客戶端。
    6. 客戶端使用令牌端點上的授權碼來請求響應。
    7. 客戶端收到響應,該響應在響應主體中包含ID令牌和訪問令牌。
    8. 客戶端驗證ID令牌並檢索終端使用者的主題識別符號。

   4.3.1 認證請求

     按照4.1.1定義進行身份驗證請求,不同的是,這些身份驗證請求引數的使用方式如下:

    • response_type值換成:code id_tokencode token, or code id_token token.
  GET /authorize?
    response_type=code%20id_token
    &client_id=s6BhdRkqt3
    &redirect_uri=https%3A%2F%2Fclient.example.org%2Fcb
    &scope=openid%20profile%20email
    &nonce=n-0S6_WzA2Mj
    &state=af0ifjsldkj HTTP/1.1
  Host: server.example.com

   4.3.1 認證請求響應

    以下是使用“混合流”成功響應

  HTTP/1.1 302 Found
  Location: https://client.example.org/cb#
    code=SplxlOBeZQQYbYS6WxSbIA
    &id_token=eyJ0 ... NiJ9.eyJ1c ... I6IjIifX0.DeWt4Qu ... ZXso
    &state=af0ifjsldkj

總結:

   OIDC的好處:

    • OIDC使得身份認證可以作為一個服務存在

    • OIDC可以很方便的實現SSO(跨頂級域)

    • OIDC相容OAuth2,可以使用Access Token控制受保護的API資源
    • OIDC可以相容眾多的IDP(身份提供商)作為OIDC的OP來使用

    • OIDC的一些敏感介面均強制要求TLS,除此之外,得益於JWT,JWS,JWE家族的安全機制,使得一些敏感資訊可以進行數字簽名、加密和驗證,進一步確保整個認證過程中的安全保障

參考

官方資料:

http://openid.net/connect/

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

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

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

   

相關文章