在上一篇[認證授權] 4.OIDC(OpenId Connect)身份認證(核心部分)中解釋了OIDC的核心部分的功能,即OIDC如何提供id token來用於認證。由於OIDC是一個協議族,如果只是簡單的只關注其核心部分其實是不足以搭建一個完整的OIDC服務的。本篇則解釋下OIDC中比較常用的幾個相關擴展協議,可以說是搭建OIDC服務必備的幾個擴展協議(在上一篇中有提到這幾個協議規范):
- Discovery:可選。發現服務,使客戶端可以動態的獲取OIDC服務相關的元數據描述信息(比如支持那些規范,接口地址是什么等等)。
- OAuth 2.0 Multiple Response Types :可選。針對OAuth2的擴展,提供幾個新的response_type。
- OAuth 2.0 Form Post Response Mode:可選。針對OAuth2的擴展,OAuth2回傳信息給客戶端是通過URL的querystring和fragment這兩種方式,這個擴展標准提供了一基於form表單的形式把數據post給客戶端的機制。
- 會話管理:Session Management :可選。Session管理,用於規范OIDC服務如何管理Session信息;Front-Channel Logout:可選。基於前端的注銷機制。
1 OIDC Discovery 規范
顧名思義,Discovery定義了一個服務發現的規范,它定義了一個api( /.well-known/openid-configuration ),這個api返回一個json數據結構,其中包含了一些OIDC中提供的服務以及其支持情況的描述信息,這樣可以使得oidc服務的RP可以不再硬編碼OIDC服務接口信息。這個api返回的示例信息如下(這里面只是一部分,更完整的信息在官方的規范中有詳細的描述和解釋說明:http://openid.net/specs/openid-connect-discovery-1_0.html):
相信大家都看得懂的,它包含有授權的url,獲取token的url,注銷token的url,以及其對OIDC的擴展功能支持的情況等等信息,這里就不再詳細解釋每一項了。
2 OAuth2 擴展:Multiple Response Types
在本系列的第一篇博客[認證授權] 1.OAuth2授權中解釋OAuth2的授權請求的時候,其請求參數中有一個 response_type 的參數,其允許的值有 code 和 token 兩個,在這兩個的基礎上,OIDC增加了一個新值 id_token (詳細信息定義在http://openid.net/specs/oauth-v2-multiple-response-types-1_0.html):
- code:oauth2定義的。用於獲取authorization_code。
- token:oauth2定義的。用戶獲取access_token。
- id_token:OIDC定義的。用戶獲取id_token。
至此OIDC是支持三種類型的response_type的,不但如此,OIDC還允許了可以組合這三種類型,即在一個response_type中包含多個值(空格分隔)。比如當參數是這樣的時候 response_type=id_token token ,OIDC服務就會把access_token和id_token一並給到調用方。OIDC對這些類型的支持情況體現在上面提到的Discovery服務中返回的response_types_supported字段中:
3 OAuth2 擴展:Form Post Response Mode
在oauth2的授權碼流程中,當response_type設置為code的時候,oauth2的授權服務會把authorization_code通過url的query部分傳遞給調用方,比如這樣“https://client.lnh.dev/oauth2-callback?code=SplxlOBeZQQYbYS6WxSbIA&state=xyz”。
在oauth2的隱式授權流程中,當response_type設置為token的時候,oauth2的授權服務會直接把access_token通過url的fragment部分傳遞給調用方,比如這樣“http://client.lnh.dev/oauth2-callback#access_token=2YotnFZFEjr1zCsicMWpAA&state=xyz&expires_in=3600”;
在oauth2中,上面的兩種情況是其默認行為,並沒有通過參數來顯示的控制。OIDC在保持oauth2的默認行為的基礎上,增加了一個名為response_mode的參數,並且增加了一種通過form表單傳遞信息的方式,即form_post(詳細信息定義在http://openid.net/specs/oauth-v2-form-post-response-mode-1_0.html)。OIDC服務對這個擴展的支持情況體現在上面提到的Discovery服務中返回的response_modes_supported字段中:
當reponse_mode設置為form_post的時候,OIDC則會返回如下的信息:
<html> <head><title>Submit This Form</title></head> <body onload="javascript:document.forms[0].submit()"> <form method="post" action="https://client.lnh.dev/oidc-callback"> <input type="hidden" name="state" value="DcP7csa3hMlvybERqcieLHrRzKBra"/> <input type="hidden" name="id_token" value="eyJhbGciOiJSUzI1NiIsImtpZCI6IjEifQ.eyJzdWIiOiJqb2huIiw iYXVkIjoiZmZzMiIsImp0aSI6ImhwQUI3RDBNbEo0c2YzVFR2cllxUkIiLC Jpc3MiOiJodHRwczpcL1wvbG9jYWxob3N0OjkwMzEiLCJpYXQiOjEzNjM5M DMxMTMsImV4cCI6MTM2MzkwMzcxMywibm9uY2UiOiIyVDFBZ2FlUlRHVE1B SnllRE1OOUlKYmdpVUciLCJhY3IiOiJ1cm46b2FzaXM6bmFtZXM6dGM6U0F NTDoyLjA6YWM6Y2xhc3NlczpQYXNzd29yZCIsImF1dGhfdGltZSI6MTM2Mz kwMDg5NH0.c9emvFayy-YJnO0kxUNQqeAoYu7sjlyulRSNrru1ySZs2qwqq wwq-Qk7LFd3iGYeUWrfjZkmyXeKKs_OtZ2tI2QQqJpcfrpAuiNuEHII-_fk IufbGNT_rfHUcY3tGGKxcvZO9uvgKgX9Vs1v04UaCOUfxRjSVlumE6fWGcq XVEKhtPadj1elk3r4zkoNt9vjUQt9NGdm1OvaZ2ONprCErBbXf1eJb4NW_h nrQ5IKXuNsQ1g9ccT5DMtZSwgDFwsHMDWMPFGax5Lw6ogjwJ4AQDrhzNCFc 0uVAwBBb772-86HpAkGWAKOK-wTC6ErRTcESRdNRe0iKb47XRXaoz5acA"/> </form> </body> </html>
這是一個會在html加載完畢后,通過一個自動提交的form表單,把id_token,access_token,authorization_code或者其他的相關數據POST到調用方指定的回調地址上。
4 OIDC 會話管理
綜合上篇提到的idtoken和前面的discovery服務以及針對oauth2的擴展,則可以讓OIDC服務的RP完成用戶認證的過程。那么如何主動的撤銷這個認證呢(也就是我們常說的退出登錄)?總結來說就是其認證的會話管理,OIDC單獨定義了3個獨立的規范來完成這件事情:
- Session Management :可選。Session管理,用於規范OIDC服務如何管理Session信息。
- Front-Channel Logout:可選。基於前端的注銷機制。
- Back-Channel Logout:可選。基於后端的注銷機制。
其中Session Management是OIDC服務自身管理會話的機制;Back-Channel Logout則是定義在純后端服務之間的一種注銷機制,應用場景不多,這里也不詳細解釋了。這里重點關注一下Front-Channel Logout這個規范(http://openid.net/specs/openid-connect-frontchannel-1_0.html),它的使用最為廣泛,其工作的具體的流程如下(結合Session Management規范):
在上圖中的2和3屬於session management這個規范的一部。其中第2步中,odic的退出登錄的地址是通過Discovery服務中返回的end_session_endpoint字段提供的RP的。其中還有一個check_session_iframe字段則是供純前端的js應用來檢查oidc的登錄狀態用的。
4567這四步則是屬於front-channel logout規范的一部分,OIDC服務的支持情況在Discovery服務中也有對應的字段描述:
4567這一部分中重點有兩個信息:
- RP退出登錄的URL地址(這個在RP注冊的時候會提供給OIDC服務);
- URL中的sessionid這個參數,這個參數一般是會包含在idtoken中給到OIDC客戶端,或者在認證完成的時候以一個獨立的sessionid的參數給到OIDC客戶端,通常來講都是會直接把它包含在IDToken中以防止被篡改。
5 總結
本篇博客介紹了OIDC的發現服務,OAuth2的兩個擴展規范,以及OIDC管理會話的機制。至此則可以構成一個完整的認證和退出的流程。其中有一點需要特別注意,這個流程中用到的token是OIDC定義的IDToken,IDToken,IDToken(重要要的事情說三遍),而不是OAuth2中定義的Access Token,千萬不要混淆這兩者,它們是有着本質的區別的(這一點在[認證授權] 3.基於OAuth2的認證(譯)和[認證授權] 4.OIDC(OpenId Connect)身份認證授權(核心部分)中都有解釋)。
6 Example
筆者基於IdentityServer3和IdentitySever4(兩者都是基於OIDC的一個.NET版本的開源實現)寫的一個集成SSO,API訪問授權控制,GIthub,QQ登錄(作為IDP)的demo:https://github.com/linianhui/oidc.example 。
參考
oidc : http://openid.net/connect/
oidc - discovery :http://openid.net/specs/openid-connect-discovery-1_0.html
oauth2 - multiple-response-types :http://openid.net/specs/oauth-v2-multiple-response-types-1_0.html
oauth2 - form-post-response-mode :http://openid.net/specs/oauth-v2-form-post-response-mode-1_0.html
oidc - session-menagement :http://openid.net/specs/openid-connect-session-1_0.html
oidc - front-channel-logout :http://openid.net/specs/openid-connect-frontchannel-1_0.html