天天看點

基于OIDC實作單點登入SSO、第三方登入[通俗易懂]背景概念1 OIDC身份認證協定2 基于OIDC實作SSO3 在OIDC的SSO中內建第三方登入(GitHub)參考文檔

大家好,又見面了,我是你們的朋友全棧君。

OIDC聯合身份認證機制

  • 背景概念
  • 1 OIDC身份認證協定
  • 2 基于OIDC實作SSO
    • 2.1 統一登入
      • 2.1.1 流程
      • 2.1.2 RP相關接口
      • 2.1.3 OP相關接口
    • 2.2 統一登出
      • 2.2.1 流程
      • 2.2.2 RP需要在向OP注冊時提供
      • 2.2.3 RP相關接口
      • 2.2.4 OP相關接口
    • 2.3 持續監視
      • 2.3.1 流程
      • 2.3.2 RP相關接口
      • 2.3.3 OP相關接口
  • 3 在OIDC的SSO中內建第三方登入(GitHub)
    • 3.1 流程
    • 3.2 RP相關接口
    • 3.3 OP相關接口
  • 參考文檔

背景概念

認證(Authentication)

認證是指應用軟體(身份資訊使用方)通過采用某種方法來确認目前請求的使用者是誰。基于密碼的認證過程可以細分為三步:

(1)認證伺服器(身份資訊提供方)從用戶端擷取使用者賬密。

(2)認證伺服器将拿到的賬密與資料庫中儲存的賬密進行比較,确認正确後,生成使用者身份資訊。

(3)使用方從提供方處擷取使用者身份資訊。

  • 當提供方與使用方能夠共享資料庫,不必跨網絡和安全邊界進行互動時,兩個角色就合并了,完成前兩步就能确認目前請求的使用者是誰,是以隻需考慮一個問題:【Q1】按照什麼流程、格式能夠安全可靠地把使用者賬密從用戶端傳遞給認證伺服器。
  • 當提供方與使用方獨立部署,必須跨網絡和安全邊界進行互動時,三步都完成後使用方才能确認目前請求的使用者是誰,是以除了【Q1】之外,還有一個問題需要考慮:【Q2】按照什麼流程、格式能夠安全可靠地把使用者身份資訊從提供方傳遞給使用方。

所有能讓使用方确認目前使用者是誰的方法都可以統稱為認證機制,應用軟體根據其部署情況、所處角色、面臨的問題來具體選擇。

  • 【Q1】的解決方案為:表單認證、HTTP Basic認證、HTTP Digest認證、HTTP Mutual認證。這些就是Web應用中常見的基于密碼的身份認證機制。
  • 【Q2】的解決方案為:OIDC、SAML 、WS-Federation、Windows AD。這些統稱為聯合身份認證機制(Federated Authentication),通常用于實作SSO、第三方登入。

單點登入(SSO)

Single Sign On,在多個互相信任的應用組成的系統中,使用者隻需在一個應用上登入一次,就可以通路系統中的所有應用。狹義的單點登入是指使用者在己方應用中登入一次就能通路己方系統中的所有應用。廣義的單點登入概念還涵蓋了第三方登入:己方應用信任第三方應用,使用者隻需在第三方應用上登入一次,就可以通路己方應用。本文中的單點登入特指其狹義概念。

第三方登入

使用者利用已有的第三方應用(IDP)賬密,來快速完成己方應用的注冊、登入。

授權(Authorization)

授權是指應用軟體(資源提供方)通過采用某種方法來确認使用者允許第三方應用(資源使用方)對他的保護資源做哪些操作。

由于資源提供方與資源使用方是兩個獨立部署的應用,必須跨網絡和安全邊界進行互動,則需考慮的問題是:

【Q3】按照什麼流程、格式能夠安全可靠地把使用者授權決策從資源使用方傳遞給資源提供方。解決方案是OAuth 2.0。

OAuth 2.0是授權協定,它規定了一套傳遞使用者授權決策的标準流程、格式。采用該流程的優點在于使用者在享受第三方應用替自己操作的便利時,不必與其共享自己的賬号密碼,第三方應用使用的是一個短期有效的通路令牌,并且使用者能夠控制令牌權限範圍,以及随時能夠讓令牌失效。具體流程是:使用者告知授權伺服器(QQ使用者中心),自己允許客戶機應用(某第三方線上PS應用)在其同意的權限範圍内(讀取某個指定相冊),通路自己儲存在資源伺服器(QQ空間)中的資料資源(照片)。顯然,授權伺服器(QQ使用者中心)必須先認證使用者的身份,才會發放通路令牌給客戶機應用(PS應用),客戶機應用憑借此通路令牌就能從資源伺服器(QQ空間)上通路使用者資料資源(讀取指定相冊的照片)。

OAuth 2.0術語

Resource Owner:資源擁有者,即使用者。

Client:客戶機應用,使用者資料資源的使用方,憑借授權憑證通路使用者存儲在資源伺服器上的特定資料資源。

AS:Authentication Server,授權伺服器,有能力對使用者進行認證、發放通路令牌、并且被資源伺服器信任的一方。

RS:Resource Server,資源伺服器,使用者資料資源的提供方。

詳解OAuth 2.0授權協定(Bearer token)

OAuth 2.0與認證機制的聯系

(1)OAuth 2.0規定了授權伺服器必須要對使用者進行認證,但它隻是授權協定,它不關心授權伺服器如何完成認證(即【Q1】),授權伺服器自行選擇認證機制。授權伺服器可以在表單認證、Basic、Digest、Mutual等密碼認證機制中進行選擇,也可以選擇非密碼的認證機制,還可以将兩者結合起來使用。

(2)從實作的角度講,OAuth 2.0能夠作為一種聯合身份認證機制來使用,但是不适合,會導緻一些問題。

由于:

  • OAuth 2.0授權流程比較安全可靠
  • 授權伺服器這個角色有能力且必須要對使用者進行認證,看起來很适合作為使用者身份資訊的提供方
  • 有時候客戶機應用也需要使用使用者身份資訊

于是有人就想,能不能利用OAuth 2.0的授權流程把使用者身份資訊從授權伺服器傳遞給客戶機應用?即利用OAuth 2.0解決【Q2】,把它作為一種聯合身份認證機制來使用。

能,Github就是這樣做的,它利用OAuth 2.0向客戶機應用同時提供使用者認證、授權兩項服務。但是,OAuth 2.0的設計本意是用來做授權的,直接拿來做使用者認證會導緻一些問題。雖然提供方(Github)能夠通過自己做額外設計來解決這些問題,但是當每個提供方采用的額外設計不一緻時,整個流程就不再統一規範了,這會給使用方帶來不必要的負擔。于是就産生了OIDC這個協定,它規定了一套标準的額外設計。

OIDC(OpenID Connect)是一個身份認證協定,它規定了一套把使用者身份資訊從授權伺服器(身份提供方)傳遞給客戶機應用(身份使用方)的标準流程、格式。

1 OIDC身份認證協定

OIDC(OpenID Connect)是關于如何使用OAuth 2.0的授權伺服器為客戶機應用提供使用者認證服務,并把對應的身份資訊傳遞給客戶機應用的标準協定。OIDC = (Identity, Authentication) + OAuth 2.0。由于它基于OAuth 2.0,當你搭建了一個OIDC的服務後,就同時擁有了認證和授權兩個功能。官網

中文版總結:

  • [認證 & 授權] 4. OIDC(OpenId Connect)身份認證(核心部分)
  • [認證 & 授權] 5. OIDC(OpenId Connect)身份認證(擴充部分)
  • OpenID Connect 協定入門指南
  • 官方認證的OIDC實作庫(C、C#、Java、JavaScript、Python、PHP、Ruby等)

OIDC術語

EU:End User:一個人類使用者。

RP:Relying Party ,使用者身份資訊的使用方(OAuth 2.0的客戶機應用)。

OP:OpenID Provider,使用者身份資訊的提供方(OAuth 2.0的授權伺服器),有能力對使用者進行認證。OP既可以是己方系統中的一個應用(使用者中心),也可以是第三方應用(IDP)。

IDP:Identity Provider,身份提供商,當OP是第三方應用時,一般稱為IDP。

OIDC的優點在于:

  1. 使得身份認證可以作為一個獨立的服務存在。
  2. 可以很友善的實作跨一級域名的SSO(把授權伺服器作為己方系統中具有單獨一級域名的的使用者中心來使用)。
  3. 可以相容OAuth 2.0、SAML、WS-Federation、Windows AD、手機短信驗證碼等衆多類型的IDP,易于內建第三方登入(把授權伺服器作為第三方登入的身份提供商來使用)。
  4. OIDC基于OAuth 2.0,可以在一個流程中同時完成授權(簽發access token)和認證(簽發id token)。
  5. 比較安全可靠,一些敏感接口均強制要求TLS,除此之外,得益于JWT,JWS,JWE的安全機制,使得一些敏感資訊可以進行數字簽名、加密和驗證,進一步確定整個認證過程中的安全性。

舉例

  • 使用者中心:大型Web應用系統,通常有A、B、C等多個一級域名,并且會把授權邏輯、使用者資訊相關邏輯獨立成一個服務,稱為使用者中心(域名為O),O不處理業務邏輯。
  • SSO:A、B、C等業務應用需要登入時,把使用者登入請求轉發給使用者中心O處理。使用者隻需在O登入一次,A、B、C就都能通路了。此時,A、B、C等業務應用就是RP,使用者中心O就是OP。
  • 對外提供第三方登入服務:使用者中心O還可以向第三方應用G提供身份認證服務。此時,第三方應用G就是RP,使用者中心O是其IDP。
  • 使用外部提供的第三方登入服務:使用者中心O也可以作為中介方,允許使用者利用已有的G應用賬密來登入A、B、C。由于G負責對使用者進行認證,O從G處擷取使用者資訊,此時O就是RP,G是其IDP。然後,O将得到的使用者資訊與自有賬号體系綁定,向A、B、C提供使用者資訊。此時,A、B、C等業務應用是RP,使用者中心O就是OP。

2 基于OIDC實作SSO

通常,SSO包括統一登入和統一登出兩個功能。涉及統一登入的标準檔案是OIDC的核心協定,涉及統一登出的的标準檔案是OIDC的三個擴充協定Front-Channel Logout、Back-Channel Logout、Session Management。

2.1 統一登入

OP:OpenID Provider,使用者身份資訊的提供方,己方系統中的使用者認證中心,域名:op.com。

RP:Relying Party ,使用者身份資訊的使用方,己方系統中的業務應用,,域名:rp.com。

本例共使用了3種認證機制:

(1)所謂【基于OIDC實作】是指作為身份使用方的RP采用了OIDC認證機制來确認目前使用者是誰。

(2)OP作為授權伺服器和身份提供方,需要驗證使用者身份,本例采用了表單認證機制。OAuth 2.0不指定,OP可以自行選擇,例如可替換為Basic、Digest、Mutual認證。

(3)OP作為授權伺服器,還需要驗證客戶機應用(RP)的身份,即确認目前請求來自于哪個客戶機應用(RP),本例采用了Basic認證機制。OAuth 2.0列舉了2種認證機制:Basic認證、POST請求體傳參(表單認證屬于這類),但是不建議使用後者,允許授權伺服器自行選擇其他認證機制。是以,可替換的選擇為:Digest、Mutual認證。

本例還采用了基于cookie的會話管理機制,可替換的選擇為:基于session、基于token、基于認證。

2.1.1 流程

1、RP要求使用者登入時,讓使用者選擇登入方式,使用者選擇使用己方系統賬密登入RP(即傳參iss=oidc_op),表單送出時觸發

GET rp.com/login?iss=oidc_op

,它将OP的授權接口

GET op.com/authorization

和所需參數組裝成完整的URI,傳回303 Location=該URI,通過浏覽器重定向。

2、OP授權接口

GET op.com/authorization

發現使用者未登入(沒有攜帶名為pyoidc的cookie),傳回303,通過浏覽器重定向到OP登入頁面。使用者輸入賬密,表單送出時觸發OP驗證賬密接口

POST op.com/user_pass/verify

(1)如果賬密錯誤,則仍然重定向到OP登入頁面。

(2)如果賬密正确,則重定向回OP授權接口(傳回303 Location=步驟1的Location),并設定OP的會話狀态(設定名為pyoidc的cookie)。

3、浏覽器攜帶cookie再度進入OP授權接口

GET op.com/authorization

,發現有pyoidc這個cookie,确認其值正确後,認為使用者已登入,于是執行授權邏輯,簽發授權碼,傳回303 Location=redirect_uri(即RP在步驟1中通過查詢參數redirect_uri提供的重定向接口),浏覽器重定向到redirect_uri。

4、RP的redirect_uri接口收到授權碼,在背景使用授權碼向OP令牌接口請求通路令牌(access token)和身份令牌(id token),使用access token向OP使用者詳情接口請求使用者詳細資訊。

2.1.2 RP相關接口

1、

GET rp.com/login?iss=oidc_op

:使用者選擇使用己方系統賬密登入(iss=oidc_op),此接口負責将OP的授權接口和所需傳參組裝成完整的URI,通過浏覽器重定向,即傳回:

HTTP/1.1 303 See Other
Location: http://op.com/authorization
?redirect_uri=http://rp.com/code_flow/oidc_op
&scope=openid+profile+email+address+phone
&response_type=code
&nonce=cdYrYNLv6wBHlBmZjWxvrQmmD
&state=DJOfvYDSDxaPzOKRoyaTaQWCoWywdeKU
&client_id=EqAfEpR492It           

複制

傳參:

  • redirect_uri:必選,使用者登入成功後,OP回傳授權碼等資訊給RP的接口。
  • scope:必選,申請擷取的資源權限,必須包含openid,表明申請擷取id token。
  • response_type:必選,希望OP采用哪種OAuth 2.0授權流程來響應,code代表授權碼流程。
  • client_id:必選,RP在OP注冊的client_id。
  • state:推薦,不透明字元串,當OP重定向到redirect_uri時,會原樣傳回給RP,用于防止CSRF、 XSRF。由于OP會原樣傳回此參數,可将state值與使用者在RP登入前最後浏覽的URI綁定,便于登入完成後将使用者重定向回最後浏覽的頁面。
  • nonce:可選,不透明字元串,當OP傳回id token時,id token中會原樣包含此值,用于減少重播攻擊。

2、

GET rp.com/code_flow/oidc_op

:重定向接口(Redirection Endpoint),即接口1中的傳參redirect_uri,需實作:

(1)接收OP回傳的code(授權碼)、state等資訊,使用授權碼在背景向OP的令牌接口

POST op.com/token

請求擷取access token和id token。其中,請求頭

Authorization

字段通過Basic關鍵字傳遞RP在OP注冊的client_id和client_secret。

POST op.com/token
Authorization: Basic cUZFeFZtQlE4blBZOjVjNWVkYjA2OTA2MTZjZGJkNGNmOWMwYjBlMjg3MWVkNjM2MzE2ZTliNjI1NWQzMDA2MDg3NGJm
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code
&redirect_uri=http://rp.com/code_flow/oidc_op
&client_id=qFExVmBQ8nPY
&state=DJOfvYDSDxaPzOKRoyaTaQWCoWywdeKU
&code=Z0FBQUFBQmVjdHFwR20wY0U5WnJ0MUxVQVhZUm1VR2UyeW14SHNLQjdxZS1fZkRhQkVkdkI0bmwyXzhDTlZ3NlVLUm51dGotRUV1TlVydG8wTjlMbEVNSjJ6ZkVnUjd4R3lsZG9pdkJNZUdRWTVkdlYzV1BEbndzUFZNWWxkY0dnVUVpaVRqaDBlaXh0dTZwbUJYQ2FmY0dabmxnY05tLVVWY2hlWXNueTM5ak5xMFdQSjgzaC1vVzhVTWsxWDZ5bWliVFRQM1IzbzR6d1dRb1RHOTF6RmI0QjFpWTdIdXNWQ0VUMjJ0cHdSRTV0UUlwWUdGMHVyUT0%3D           

複制

(2)接收OP令牌接口傳回的access token和id token等資訊。使用state、OP提供的簽名秘鑰等資訊校驗id token真實性。

(3)使用access token在背景向OP的使用者詳情接口

GET op.com/userinfo

發請求,擷取使用者詳細資訊。其中請求頭

Authorization

字段使用

Bearer

關鍵字傳遞access token。

GET op.com/userinfo
Authorization: Bearer Z0FBQUFBQmVjdHFwcW1Xc08ybG9BaG5PRGNJSTR3alFjTjJEcEF4aVl3VDZCMW5OTmFhOXdZeUR2bm4tX2d5U0Y0Z0Y0T2tFMWFZQ2dTaG1MSmdjZjlrOHlHNml5SXQ5R2RqVGN5SE84WjNmSkFfZ3MycTZMZXlQYXd2Nm0wZXhiMVkzeVNORGtHRExIN2xYX0twbkJfVmg2Wks3bGwtOTVvbEV3ajZuQWVETVFDYm8tWlZTZC1SdjVNMHRJU3c2QlYtTFp0a2wtQkVBMWFwZDJqXzJmZkRVT1Fka2I2ck9nLWFWRzRTaGVHN0Y4ZkZobmNHZ0FCaz0=           

複制

(4)RP建構自身的會話狀态(設定一個cookie,表明表明使用者已在RP登入)。

(5)找出state綁定的URI,将使用者重定向回登入前最後浏覽的頁面。

2.1.3 OP相關接口

1、

POST op.com/registration

:RP向OP進行注冊的接口,OP傳回client_id和client_secret給RP。

2、

GET op.com/static/jwks.json

:OP提供JWT簽名秘鑰的接口。

3、

GET op.com/authorization

:授權接口(Authorization Endpoint),需實作:

(1)接收并校驗RP在查詢參數中傳入的redirect_uri、scope、response_type、client_id、state、nonce。如果校驗失敗,傳回OIDC規定的錯誤響應。(本例将這個帶有查詢參數的完整URI稱為authz_uri,後面會用到它)

(2)檢查使用者是否已在OP登入(檢查名為pyoidc的cookie)。如果未登入,則重定向到OP登入頁面

GET op.com/login.html?authz_uri=...

(在查詢參數中傳入authz_uri);如果已登入,則執行授權邏輯,将授權碼等回傳參數與RP提供的redirect_uri組裝成完整URI,通過浏覽器重定向,即傳回:

HTTP/1.1 303 See Other
Location: http://rp.com/code_flow/oidc_op
?state=DJOfvYDSDxaPzOKRoyaTaQWCoWywdeKU
&scope=openid+profile+email+address+phone
&code=Z0FBQUFBQmVjc2ExRTJvZmpGN1FNTERzV0NQOUVieHRHZUdLdUd5V0dvZUMzTzliS3hjeUVpVUpxN21GWWhhaTlvalVyblVOVXI2XzJVZG1vZlctNkRNZlpTanpFM2hVSzdKaWliSDUxX1RhcW5pUk9ScTRMNW1icUh6WlJGamxIWkJhbmFnakhxbUdTenJpZVowQ3dEbDh5c3Z1ZDBpOWFGSXBtMHBRSk1IUFdPTVBxUmtzUnEzVHFCWDlYMDhXUkItWXVWczkwT0Fjb1M1bktIalZCUUdSd2p3b2lnUGZYazhEODdYekhnRTJVdzZjRkR3U01SUT0%3D
&iss=http%3A%2F%2Fop.com
&client_id=EqAfEpR492It           

複制

傳參:

  • state:将RP傳入的state原樣傳回。
  • code:OP簽發的授權碼。
  • scope:OP準許的資源權限。
  • iss:授權碼的簽發人,即OP的域名。
  • client_id:授權碼的簽發對象,即RP的client_id。

4、

POST op.com/user_pass/verify

:驗證賬密接口,使用者在OP登入頁面輸入賬密,表單送出時觸發此接口。其入參為賬密和authz_uri,負責進行表單認證:

(1)如果賬密錯誤,則仍然重定向回OP登入頁面。

(2)如果賬密正确,則建構OP自身的會話狀态(設定名為pyoidc的cookie),通過浏覽器重定向到authz_uri,即傳回:

HTTP/1.1 303 See Other
Set-Cookie: pyoidc="1584580277|BEWREbvcxoKRTD/6bin6mA==|eeu5GgtXvmGysKMmRDWISee85yZZolGc0zBgdQ==|y/NQfIBzH01PcdK+BzBPXA=="; expires=Thu, 19-Mar-2020 01:16:17 GMT; HttpOnly; path=/
Location: http://op.com/authorization
redirect_uri=http://rp.com/code_flow/oidc_op
&scope=openid+profile+email+address+phone
&response_type=code
&nonce=cdYrYNLv6wBHlBmZjWxvrQmm
&state=DJOfvYDSDxaPzOKRoyaTaQWCoWywdeKU
&client_id=EqAfEpR492It           

複制

5、

POST op.com/token

:令牌接口(Token Endpoint),需實作:

(1)校驗RP在請求頭

Authorization

字段通過HTTP Basic認證傳入的client_id和client_secret。

(2)校驗RP在請求體中傳入的code、grant_type、state、redirect_uri、client_id等參數。如果校驗失敗,傳回OIDC規定的錯誤響應。

(3)如果都校驗通過,則生成access token、id token并傳回。

HTTP/1.1 200 OK
Content-Type: application/json

{ 
   
	"state": "DJOfvYDSDxaPzOKRoyaTaQWCoWywdeKU", 
	"scope": "openid profile email address phone", 
	"access_token":"Z0FBQUFBQmVjdWxLcFBWVnFzMmJ3ckcxa1ptM21kS1lDd01rY0hsVUk1WFlkUTk2MXlrN3BkdmpjTGx0VGxFeXg0SlNfVWExNldqeExRZ0RhdWt6b1Ewdy12aWlueTRMc29sc01WOHg1QXhxWjFiU1BBMmhGX2FQZnZhSUJmQVJ4eUphZ0xWRjk3M3M5MUZ0eTlVZEJxOXFtSGNuZlNaS3BqU0ZKSFBoZXE1dWdjOE13alZhLTh3Z1NVQ191V3p5a2tCSjRsT01fYVVFTV9DQmc5UFFVZnBiUmVHSXJvdzdVYzctMzJwaHVoWkFDem5MUmo4YnB2WT0=", 
	"token_type": "Bearer", 
	"id_token":"eyJhbGciOiJSUzI1NiIsImtpZCI6InZPQnl2cjBRbTBfVUI4RFZwMkRjRVNUeWt0OHZ5bV9PS1F2VjBhRm1yQWMifQ.eyJpc3MiOiAiaHR0cDovLzEyNy4wLjAuMTo4MDAyIiwgInN1YiI6ICIwMTFkYmUwMWFhYjZkZDQ1MWJlNDIzYmI0ZDI0MWFlOWM0OTA0YzJjN2FkOTk0Yzk4YmRhOGRkNmI3Y2VhYWYwIiwgImF1ZCI6IFsicTg5TFBjNkkwQTRqIl0sICJleHAiOiAxNTg0Njc1NTMwLCAiYWNyIjogInBhc3N3b3JkIiwgImlhdCI6IDE1ODQ1ODkxMzAsICJzaWQiOiAiZGNiN2E0ODlmM2JkNWY1Y2E0YmViZGFhYWY3ZjczMzY4ZjJkZTRhMDQ4YzYwNGUyMjBmOTRhNTQiLCAibm9uY2UiOiAiVzR6NldpUzF1djlwME1jMnJaNWc1Yng3In0.Jfvl4aCJy54YRbWobj7ozQUkfA2XezHtDwZhu7t5cNaguUuxNJ-epGTaub3DfmGcXI__CB_BXuQ-phWXqbz7YQ0jbwk6HtO6pGJCHfxGmcEHisM0z_-6BwJVrm6JbVw90m4zdmen5F_palkHyI4giYtrbNA8bIAraG-pZ5jZRJOmTIWHNGKopIHhUuzv39H1Ydgn5WROgz9lk24vHmyqiXiyCl2GXFcso6tEHtU9rM5oaGbIrZb6M0HfbxgmoagAw9Z9yG3p6DDihsiHUjWVccZ8o_IwS6NfJb16WFE2NoGlUBvv3Vt7VFoJJlNtTSjc7CMCij1p8k_FiN7nPMoq8w"
	}           

複制

6、

GET op.com/userinfo

:使用者詳情接口(UserInfo Endpoint),需實作:

(1)校驗RP在請求頭

Authorization

字段中通過

Bearer

關鍵字傳入的access token。如果校驗失敗,傳回OIDC規定的錯誤響應。

(2)如果校驗通過,傳回使用者詳細資訊。

2.2 統一登出

OIDC通過擴充協定提供了兩種登出機制:Front-Channel Logout 和 Back-Channel Logout 。如果OP同時支援兩種登出機制,RP在注冊時任選一種即可。

2.2.1 流程

1、使用者點選登出時,觸發請求

GET rp.com/logout

,RP清除該使用者的會話狀态,重定向到OP的統一登出接口(End Session Endpoint)。該RP稱為主動登出RP。

2、OP的統一登出接口

GET op.com/end_session

負責生成一個包含uid、sid、client_id、redirect_uri等資訊的JWT,再重定向到OP的登出驗證接口。

3、OP的登出驗證接口

GET op.com/logout_verify

解析JWT,根據sid、client_id查找出所有該使用者已登入的RP資訊,逐一通知這些RP被動登出,同時清除該使用者在OP的會話狀态。最後重定向到主動登出RP提供的post_logout_redirect_uri,通知它使用者已成功登出。

4、主動登出RP收到對post_logout_redirect_uri的請求時,傳回退出成功的頁面給使用者。

6、被動登出RP收到對frontchannel_logout_uri或backchannel_logout_uri的請求時,清除該使用者的會話狀态。

7、當使用者重新整理被動登出RP的頁面時,頁面提示使用者已登出。

2.2.2 RP需要在向OP注冊時提供

1、post_logout_redirect_uri:必選,主動登出時接收OP成功登出通知的接口。

2、兩種登出機制任選其一:

  • 如果采取frontchannel登出機制,則額外提供:

    (1)frontchannel_logout_uri:必選,被動登出時接收OP登出通知的接口。

    (2)frontchannel_logout_session_required:可選,值為True表示要求OP在回調frontchannel_logout_uri時提供傳參iss、sid,否則不傳。

  • 如果采取backchannel登出機制,則額外提供:

    (1)backchannel_logout_uri:必選,被動登出時接收OP登出通知的接口。

    (2)backchannel_logout_session_required:可選,值為True表示要求OP在回調backchannel_logout_uri時提供傳參iss、sid,否則不傳。

2.2.3 RP相關接口

1、

GET rp.com/logout

:使用者點選登出時,觸發此接口,需實作:

(1)清除該使用者的會話狀态(将RP指定cookie值設定為空)。

(2)将OP的統一登出接口和所需參數組裝成完整的URI,通過浏覽器重定向,即傳回:

HTTP/1.1 303 See Other
Location: http://op.com/end_session?id_token_hint=eyJhbGciOiJSUzI1NiIsImtpZCI6InZPQnl2cjBRbTBfVUI4RFZwMkRjRVNUeWt0OHZ5bV9PS1F2VjBhRm1yQWMifQ.eyJpc3MiOiAiaHR0cDovLzEyNy4wLjAuMTo4MDAyIiwgInN1YiI6ICIwMTFkYmUwMWFhYjZkZDQ1MWJlNDIzYmI0ZDI0MWFlOWM0OTA0YzJjN2FkOTk0Yzk4YmRhOGRkNmI3Y2VhYWYwIiwgImF1ZCI6IFsibWg2OVdDanR0YTVYIl0sICJleHAiOiAxNTg0NjkzMjgzLCAiYWNyIjogInBhc3N3b3JkIiwgImlhdCI6IDE1ODQ2MDY4ODMsICJzaWQiOiAiOWYxZTRhMjIwZDIwOGQwYWUzMTFmYzUxMzNlNTlhYzFmNDAyNjM0ZTA2ZDE2YjVmZTIwZmI2OTIiLCAibm9uY2UiOiAiSmEweXZESjFuQ1VjQjNwQk1xOXZvN09KIn0.HL56KJiE11oEZNWFai3BJczcmXHoPzizlfOTSl2q0cpUjKE9Y-Ku6O968Fbcm_fwLVTHkJBiSmmqv9DIGFa10SUuTl6Yc7rWUKgwUsIKavQ16OMBwX2XQ2TGGEq5DAodSpbdJegPfX9Q5mjk4-NfCW2aLC9fNJN-6wQtq8wZSDyc4pHUtkufioA5f114RMgMHtmg2_ws-gN1PN_twKsidzi8BFKHg2QRHUV_8-WxzS4nQPTvWIH4YdmesexZ8_VgmGQx6NtTqiVlNq_ojz50r1KaQ3lJ8PBG9X1iMxElRrjEE1ZH-4lQ9db_lDmVbCn-hYvt1QtbbYa9Oi641J0dHg
&post_logout_redirect_uri=http%3A%2F%2F127.0.0.1%3A8001%2Flogout_success%2Foidc_op           

複制

傳參:

  • post_logout_redirect_uri:必選,當使用者在OP成功登出後,OP會對主動登出RP回調此接口,以通知RP,必須與注冊時提供的post_logout_redirect_uri一緻。
  • id_token_hint:必選,RP将統一登入時收到的id token作為id_token_hint提供給OP,以告知OP是哪個使用者要登出。

    (這兩個參數是在OIDC擴充協定Session Management中規定的)

2、

GET rp.com/logout_success

:即RP提供的post_logout_redirect_uri, 當OP回調時,傳回退出成功的頁面給使用者。

3、

GET rp.com/frontchannel_logout_callback

:采取frontchannel登出機制,RP被動登出時接收OP登出通知的接口,需實作:

(1)如果OP回調此接口時提供傳參iss、sid,則校驗。如果校驗失敗,傳回OIDC規定的錯誤響應。

(2)清除該使用者的會話狀态(将RP指定cookie值設定為空)。

4、

POST rp.com/backchannel_logout_callback

:采取backchannel登出機制,RP被動登出時接收OP登出通知的接口,需實作:

(1)接收OP通過請求體傳入的參數logout_token,使用OP提供的簽名秘鑰等資訊校驗logout_token真實性。

(2)如果logout_token中包含了iss、sid,則校驗。如果校驗失敗,傳回OIDC規定的錯誤響應。

(3)清除該使用者的會話狀态(将RP指定cookie值設定為空)。

2.2.4 OP相關接口

1、

GET op.com/end_session

:統一登出接口,負責生成一個包含uid、sid、client_id、redirect_uri等資訊的JWT,重定向到OP的登出驗證接口。

2、

GET op.com/logout_verify

:登出驗證接口,需實作:

(1)接收并解析JWT,根據sid、client_id查找出所有該使用者已登入的RP資訊,逐一通知這些RP被動登出。

  • 如果RP注冊時提供的是frontchannel_logout_uri,則構造帶iframe的html,iframe的src=frontchannel_logout_uri(即OP讓浏覽器通知所有采取frontchannel登出機制的RP被動登出)。
  • 如果RP注冊時提供的是backchannel_logout_uri,則在背景發POST請求給backchannel_logout_uri,攜帶logout_token(即OP自己通知所有采取backchannel登出機制的RP被動登出)

(2)清除該使用者的會話狀态(OP将名為pyoidc的cookie值設定為空)。

(3)無論RP采取frontchannel、還是backchannel登出機制,此接口最後傳回的都是一個HTML,其中包含一個js腳本檔案,在頁面加載完成後,跳轉到主動登出RP提供的post_logout_redirect_uri。

2.3 持續監視

OIDC的擴充協定Session Management規定了RP如何持續監視使用者在OP登入狀态的方法。此擴充協定既可以與兩種登出機制分開使用,也可以結合使用。特别是對于沒有服務端的純JS應用,兩種登出機制都無法使用,則可以通過此擴充協定提供的方法持續監視使用者動态,實作被動登出。

2.3.1 流程

1、OP 的授權接口

GET op.com/authorization

在重定向到redirect_uri(

GET rp.com/code_flow

)并傳回授權碼等資訊給RP的同時(即統一登入流程步驟3),額外提供session_state這個傳參給RP,代表使用者在OP的登入狀态,OP同時設定一個名為pyoic_session的cookie用來提供資訊給後面的op_iframe使用。

2、RP的redirect_uri(

GET rp.com/code_flow

)将收到的session_state儲存起來,然後構造一個HTML作為響應傳回,HTML中包含兩個用于監視會話狀态的iframe:rp_iframe(

GET rp.com/session_iframe

)和op_iframe(

GET op.com/check_session_iframe

),HTML中還包含一個js腳本檔案,在頁面加載完成後,跳轉到使用者登入前最後浏覽的頁面。

3、op_iframe(

GET op.com/check_session_iframe

),負責通過postMessage接收rp_iframe發來的消息,然後讀取OP的pyoic_session這個cookie,提取所需資訊,計算正确的session_state值,并與rp_iframe發來的session_state值進行比較。如果兩者相同,則傳回unchanged,否則傳回changed。

4、rp_iframe負責按照設定的時間間隔使用 postMessage 發送指定消息輪詢op_iframe,進而持續監視使用者在OP的登入狀态是否變化。如果從op_iframe收到的傳回為unchanged,則繼續監視。如果收到的是changed,則調用

GET rp.com/session_change

這個接口進行處理。

5、

GET rp.com/session_change

負責将OP 的授權接口與所需傳參組裝成完整的URI,與之前(即統一登入流程步驟1)的傳參相比,額外提供了prompt和id_token_hint,通過浏覽器重定向到此URI。

6、OP的授權接口

GET op.com/authorization

将照常響應該請求(統一登入流程步驟3),再次重定向到redirect_uri(

GET rp.com/code_flow

),此時

GET rp.com/code_flow

将做:

(1)如果收到的新id_token所代表的使用者與原有id_token所代表的使用者相同,表明使用者在OP仍處于正常登入狀态,則儲存新的session_state值,重複步驟1-4,重新開始監視。

(2)反之,如果新id_token所代表的使用者不同,或者沒有收到新id_token等異常情況,則視同使用者已在OP中登出,清除該使用者在RP的會話狀态。

2.3.2 RP相關接口

1、

GET rp.com/code_flow

:此接口在原功能(統一登入RP相關接口3)基礎上,需額外實作:

(1)将session_state、client_id、issuer這三個資訊單獨儲存起來,便于後續使用。

(2)構造一個HTML作為響應傳回,HTML中包含兩個隐藏的iframe:

rp_iframe:負責調用

GET rp.com/session_iframe

這個接口。

op_iframe:負責調用

GET op.com/check_session_iframe

這個接口。

(3)HTML中還包含一個js腳本檔案,在頁面加載完成後,跳轉到使用者登入前最後浏覽的頁面。

(4)當OP再次重定向到此接口(持續監視流程6),傳入更新的id_token和session_state值時,對新的id_token進行校驗,如果收到的新id_token所代表的使用者與舊id_token所代表的使用者相同,則儲存新的session_state值。最後傳回構造的HTML。

(5)反之,如果校驗失敗,或者新id_token所代表的使用者不同,或者沒有收到新id_token等異常情況,則應視同使用者已在OP中登出,清除該使用者在RP的會話狀态(将RP指定cookie值設定為空)。

2、

GET rp.com/session_iframe

:擷取rp_iframe的接口,需實作:

(1)找到已儲存的session_state、client_id、issuer這三個資訊。

(2)構造一個HTML作為響應傳回,這個HTML就是rp_iframe,負責按照設定的時間間隔使用 postMessage 發送指定消息輪詢op_iframe,進而持續監視使用者在OP的登入狀态是否變化。指定消息為

var message = client_id + " " + session_state

,如果從op_iframe收到的傳回為unchanged,則繼續監視。如果收到的是changed,則調用

GET rp.com/session_change

3、

GET rp.com/session_change

:當rp_iframe檢測到會話狀态發生變化時,調用此接口進行處理,需實作:

(1)将OP的授權接口和所需傳參組裝成完整的URI,通過浏覽器重定向,即傳回:

HTTP/1.1 303 See Other
Location: http://op.com/authorization
?redirect_uri=http://rp.com/code_flow/oidc_op
&scope=openid+profile+email+address+phone
&response_type=code
&nonce=cdYrYNLv6wBHlBmZjWxvrQmmD
&state=DJOfvYDSDxaPzOKRoyaTaQWCoWywdeKU
&client_id=EqAfEpR492It
&prompt=none
&id_token_hint=eyJhbGciOiJSUzI1NiIsImtpZCI6InZPQnl2cjBRbTBfVUI4RFZwMkRjRVNUeWt0OHZ5bV9PS1F2VjBhRm1yQWMifQ.eyJpc3MiOiAiaHR0cDovLzEyNy4wLjAuMTo4MDAyIiwgInN1YiI6ICIwMTFkYmUwMWFhYjZkZDQ1MWJlNDIzYmI0ZDI0MWFlOWM0OTA0YzJjN2FkOTk0Yzk4YmRhOGRkNmI3Y2VhYWYwIiwgImF1ZCI6IFsiNVRsMFhFbWNhZnBlIl0sICJleHAiOiAxNTg0NzE2MjkzLCAiYWNyIjogInBhc3N3b3JkIiwgImlhdCI6IDE1ODQ2Mjk4OTMsICJzaWQiOiAiYjg5NGRkZmZmMDgyOGY0YjRkMzg3NzE2MzcyY2ViZGVhZjg1ZjNkZWM3Nzk2OGExYjc2MTg1NjMiLCAibm9uY2UiOiAiSkhRS0w3Um0wZ2FCSW9oakpPRHZVcEZaIn0.Ma14vJMfTUBmQlG0vduKoUAjzMnhrQEn6FYTP5QQbAC4imJZWovqlgyijJpVbf9B3oevX4zc_i-19Stb7CdHVy9k_zuTYN7RRgo60Oz7k4WnAU-4NdmcmHD1MSHJUJpSLZcTPL81kc_YNGHHvxgOW217OOfF1yqfQADyvbwI4GNq05rq4R3mlkt3wdZGQaewJKC1qSMckaEAgtWbE4Iaj71uIoHuG_KHJdoY2CxSvbzBV55AN8soOL7kiPWfn6uIuDJVIFGTlL9ufRKogh7gDbKbUQ91q5yY1ARfP70z8LCnFI1ceLqJuJALa5FItRnayfO_RI2txVoGrWDyOd1F_g           

複制

額外提供的2個傳參:

  • prompt=none,代表OP收到此請求時不用通過授權頁面告知使用者,此請求僅用于擷取更新的id_token值和session_state值。
  • id_token_hint=RP統一登入時收到的的id_token值,用于告知OP想要擷取哪個使用者的新id_token值和session_state值。

2.3.3 OP相關接口

1、

GET op.com/authorization

:授權接口在原功能(統一登入OP相關接口3)基礎上,需額外實作:

(1)額外提供session_state這個傳參給RP,代表使用者在OP的登入狀态,它對RP來說是不透明的。

(2)設定一個名為pyoic_session的cookie用來提供資訊給後面的op_iframe使用。

2、

GET op.com/check_session_iframe

:擷取op_iframe的接口,需實作:

直接傳回HTML作為響應,這個HTML就是op_iframe,負責通過postMessage接收rp_iframe發來的消息(

client_id + " " + session_state

),然後讀取OP自身的pyoic_session這個cookie,提取所需資訊,計算正确的session_state值,并與rp_iframe發來的session_state值進行比較。如果兩者相同,則傳回unchanged,否則傳回changed。

3 在OIDC的SSO中內建第三方登入(GitHub)

上面的例子在統一登入時采用的是己方系統的賬密。OIDC的優點之一是可以相容衆多的IDP(身份提供商),易于內建第三方登入,SSO的RP隻需做出非常微小的改動。本例使用GitHub OAuth 2.0作為IDP,但是OIDC支援的IDP類型不局限于OAuth 2.0,它還支援SAML,WS-Federation,Windows AD,或者常用的手機短信驗證碼等。這是因為OIDC并不關心OP如何完成使用者認證(【Q1】),它關心的隻是如何把使用者身份資訊安全可靠地從OP傳遞給RP(【Q2】)。

本例涉及的認證機制:

(1)rp.com作為身份使用方,采用了OIDC認證機制,同上例。

(2)op.com作為授權伺服器,需要驗證客戶機應用(rp.com)的身份,本例采用了Basic認證機制,同上例。

(3)op.com作為身份使用方(中介),采用了OIDC認證機制。

(3)GitHub作為身份提供方,需要驗證使用者身份,GitHub采用的是表單認證機制。

(4)GitHub作為授權伺服器,需要驗證客戶機應用(op.com)身份,GitHub同時支援Basic、POST請求體傳參兩種認證機制,客戶機應用(op.com)可以任選。

3.1 流程

1、RP要求使用者登入時,讓使用者選擇登入方式,使用者選擇使用Github登入(即傳參iss=github),表單送出時觸發

GET rp.com/login?iss=github

,它将OP的授權接口和所需參數(RP的redirect_uri:

GET http://rp.com/code_flow/oidc_op

)組裝成完整的URI(本例稱其為authz_uri),通過浏覽器重定向。

2、OP授權接口

GET op.com/authorization

發現使用者未在OP登入(沒有攜帶名為pyoidc的cookie),于是通過Github對使用者進行身份認證,即将Github授權接口

GET https://github.com/login/oauth/authorize

和所需參數(OP的redirect_uri:

GET op.com/github/verify

)組裝成完整的URI,通過浏覽器重定向,由Github對使用者進行身份認證。使用者在Github登入并授權後,Github再通過浏覽器重定向到OP的redirect_uri,同時提供code(授權碼)和state。

3、OP的redirect_uri(

GET op.com/github/verify

)使用收到的授權碼向Github令牌接口請求access token,使用access token向Github使用者詳情接口請求使用者詳細資訊并綁定到自有賬号體系上。至此使用者認證通過,OP設定使用者的會話狀态(設定名為pyoidc的cookie),并重定向到authz_uri。

4、浏覽器攜帶cookie重定向,再度進入OP授權接口

GET op.com/authorization

,發現使用者已在OP登入(名為pyoidc的cookie校驗通過),于是執行授權邏輯,簽發OP授權碼,重定向到RP的redirect_uri(RP在步驟1中提供的)。

5、RP的redirect_uri接收到OP的授權碼,使用授權碼向OP的令牌接口請求access token和id token,使用access token向OP的使用者詳情接口請求使用者詳細資訊。

3.2 RP相關接口

1、

GET rp.com/login?iss=github

:傳參iss=github表明使用者選擇使用Github第三方登入,此接口負責将OP的授權接口和所需參數組裝成完整的URI,通過浏覽器重定向,即傳回:

HTTP/1.1 303 See Other
Location: http://op.com/authorization
?redirect_uri=http://rp.com/code_flow/oidc_op
&scope=openid+profile+email+address+phone
&response_type=code
&nonce=qku5JVdjlKjnbsF5hpRBJwdz
&state=c7HBU6Sb1nAcWELJx6l2aUsQfnwIl0EW
&client_id=EqAfEpR492It
&acr_values=github           

複制

額外傳參:

  • acr_values:表明要求OP使用github作為IDP(身份認證提供商)。

由此可見,RP的改動非常小,隻需一個參數(acr_values=github)就能實作Github登入,如果OP還支援微信登入,那麼RP隻需傳遞acr_values=wechat就夠了。

3.3 OP相關接口

儲存IDP(Github)的配置資訊:

(1)授權接口(Authorization Endpoint):

GET https://github.com/login/oauth/authorize

(2)令牌接口(Token EndPoint):

POST https://github.com/login/oauth/access_token

(3)使用者詳情接口(UserInfo EndPoint):

GET https://api.github.com/user

(4)client_id、client_secret:需手動向Github注冊擷取:https://github.com/settings/applications/new

(5)scope:确定需要向Github申請哪些資源權限。Github提供的可選scope

1、

GET op.com/authorization

:授權接口在原功能(統一登入OP相關接口3)基礎上,需額外實作:

(1)找出傳參acr_values指定的IDP(Github)的配置資訊,将Github的授權接口和所需傳參組裝成完整的URI,通過浏覽器重定向,即傳回:

HTTP/1.1 303 See Other
Location: https://github.com/login/oauth/authorize
?redirect_uri=http://op.com/github/verify
&scope=public_repo+user
&state=c7HBU6Sb1nAcWELJx6l2aUsQfnwIl0EW
&client_id=9c21477eb0a5e2191342           

複制

傳參:

  • redirect_uri:OP的redirect_uri:

    GET op.com/github/verify

  • client_id:OP在Github注冊的client_id
  • scope:在Github提供的scope值中選擇的
  • state:為了友善,傳參state沿用了RP提供的state
  • 由于Github對于Web應用隻支援授權碼流程(authorization code grant type),是以它不要求提供response_type。

Github授權接口支援的傳參:

基于OIDC實作單點登入SSO、第三方登入[通俗易懂]背景概念1 OIDC身份認證協定2 基于OIDC實作SSO3 在OIDC的SSO中內建第三方登入(GitHub)參考文檔

4、

GET op.com/github/verify

:即OP的redirect_uri,此接口需實作:

(1)接收GitHub回傳的code(授權碼)和state,并校驗state值。

(2)使用授權碼在背景向GitHub的令牌接口

POST https://github.com/login/oauth/access_token

請求access_token。

Github令牌接口支援的傳參:

基于OIDC實作單點登入SSO、第三方登入[通俗易懂]背景概念1 OIDC身份認證協定2 基于OIDC實作SSO3 在OIDC的SSO中內建第三方登入(GitHub)參考文檔

注意:

  • client_id、client_secret可以通過POST請求體傳入,也可以通過請求頭Authorization的HTTP Basic認證傳入,GitHub令牌接口同時支援這兩種方式。
  • GitHub采用的不是标準的OIDC協定,它的令牌接口隻會傳回access token,而不會傳回id token。
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8

{ 
   "access_token":"e72e16c7e42f292c6912e7710c838347ae178b4a", "scope":"public_repo,user", "token_type":"bearer"}           

複制

(3)使用access_token在背景向GitHub的使用者詳情接口

GET https://api.github.com/user

請求使用者詳細資訊。通過請求頭

Authorization

Bearer

關鍵字傳遞access token。

(4)将使用者詳細資訊與自有賬号體系綁定。

參考文檔

  1. [認證 & 授權] 4. OIDC(OpenId Connect)身份認證(核心部分)
  2. [認證 & 授權] 5. OIDC(OpenId Connect)身份認證(擴充部分)
  3. OpenID Connect 協定入門指南
  4. 官方認證的OIDC實作庫(C、C#、Java、JavaScript、Python、PHP、Ruby等)
  5. [OIDC in Action] 1. 基于OIDC(OpenID Connect)的SSO
  6. [OIDC in Action] 2. 基于OIDC(OpenID Connect)的SSO(純JS用戶端)
  7. [OIDC in Action] 3. 基于OIDC(OpenID Connect)的SSO(添加Github OAuth 2.0的支援)
  8. GitHub OAuth 第三方登入示例教程
  9. 淺談SAML, OAuth, OpenID和SSO, JWT和Session
  10. 認證與授權——單點登入協定盤點:OpenID vs OAuth2 vs SAML
  11. JSON Web Token 入門教程
  12. OpenID Connect Federation 入門指南
  13. Web應用中基于密碼的身份認證機制(表單認證、HTTP認證: Basic、Digest、Mutual)
  14. OAuth 2.0授權協定(Bearer token)

釋出者:全棧程式員棧長,轉載請注明出處:https://javaforall.cn/179103.html原文連結:https://javaforall.cn