OIDC協(xié)議
OIDC(OpenID Connect)是在OAuth2上構建了一個身份層,是一個基于OAuth2協(xié)議的身份認證標準協(xié)議。
OAuth2協(xié)議
OAuth2是一個授權協(xié)議,它無法提供完善的身份認證功能【1】,OIDC使用OAuth2的授權服務器來為第三方客戶端提供用戶的身份認證,并把對應的身份認證信息傳遞給客戶端。
使用OAuth2進行認證的常見誤區(qū)
如果用OAuth2進行認證,會有許多問題。
- 在OAuth2中,
token
被設計成是對客戶端
不透明的,但在用戶身份認證的上下文環(huán)境中,客戶端
往往需要能夠從token
中派生一些信息。這可以通過定義一個雙重目的(dual-purposing)的客戶端
以解析和理解的token
來完成。但是OAuth2協(xié)議并沒有為access token
本身定義特定的格式貨結構,因此需要在OAuth2協(xié)議的基礎上,利用諸如OpenId Connect的ID Token
在響應中提供一個次要的標記,它將和access token
一起發(fā)送給客戶端
中。一方面,保持了token
是對客戶端
的不透明,另一方面,為客戶端
提供了其所需的認證信息。 - OAuth保護的是資源,獲取用戶屬性的API(identity API)通常沒有辦法告訴你用戶是否存在。而且,在某些情況下,用戶無需身份驗證即可獲得access token(比如[認證授權] 1.OAuth2授權 - 5.4 Client Credentials Grant)。也就是說
Access Token
并不能和某個用戶
關聯(lián)起來。 - 在某些場合,例如,使用implicit流程(這個流程中直接把acces token作為url的hash參數(shù)([認證授權] 1.OAuth2 授權 - 5.2.2 Access Token Response))中,
User Agent
可以獲得token
,也就你開辟了一個注入access token
到應用程序外部(并可能在應用程序外部泄露)的地方。如果Client不通過某種機制驗證access token,則它無法區(qū)分access token是有效的令牌還是攻擊的令牌。 - 很可能有一個幼稚的(naive)
客戶端
,從其他的客戶端
拿到一個有效的token
來作為自己的登錄事件。畢竟token
是有效的,對API的訪問也會返回有效的用戶
信息。問題在于沒有用戶做任何事情來證明用戶
存在,在這種情況下,用戶
甚至都沒有授權給幼稚的(naive)客戶端
。 - 如果攻擊者能夠攔截或者替換來自Client的一個調用,它可能會改變返回的用戶信息,而客戶端卻無法感知這一情況。這將允許攻擊者通過簡單地在正確的調用序列中交換用戶標識符來模擬一個幼稚的(naive)Client上的用戶。
- 基于OAuth 身份(identity)API的最大問題在于,即使使用完全符合OAuth的機制,不同的提供程序不可避免的會使用不同的方式實現(xiàn)身份(identity)API。
OIDC核心概念:ID Token
OAuth2提供了Access Token
來解決授權第三方客戶端
訪問受保護資源的問題;OIDC在這個基礎上提供了ID Token
來解決第三方客戶端標識用戶身份認證的問題。OIDC的核心在于在OAuth2的授權流程中,一并提供用戶的身份認證信息(ID Token
)給到第三方客戶端
,ID Token
使用JWT
格式來包裝。此外還提供了UserInfo的
接口,用戶獲取用戶的更完整的信息。
參與角色
主要的術語以及概念介紹:
- EU:End User:一個人類
用戶
。 - RP:Relying Party ,用來代指OAuth2中的受信任的
客戶端
,身份認證和授權信息的消費方; - OP:OpenID Provider,有能力提供EU認證的服務(比如OAuth2中的授權服務),用來為RP提供EU的身份認證信息;
- ID Token:JWT格式的數(shù)據(jù),包含EU身份認證的信息。
- UserInfo Endpoint:用戶信息接口(受OAuth2保護),當RP使用
Access Token
訪問時,返回授權用戶的信息,此接口必須使用HTTPS。
工作流程
OIDC的流程由以下5個步驟構成:
-
RP
發(fā)送一個認證請求給OP
; -
OP
對EU
進行身份認證,然后提供授權; -
OP
把ID Token
和Access Token
(需要的話)返回給RP
; -
RP
使用Access Token
發(fā)送一個請求UserInfo EndPoint
; -
UserInfo EndPoint
返回EU
的Claims
。
oidc.jpg
注意這里面RP
發(fā)往OP
的請求,是屬于Authentication
類型的請求,雖然在OIDC中是復用OAuth2的Authorization
請求通道,但是用途是不一樣的,且OIDC的AuthN請求中scope參數(shù)必須要有一個值為的openid
的參數(shù),用來區(qū)分這是一個OIDC的Authentication
請求,而不是OAuth2的Authorization
請求。
什么是ID Token
OIDC對OAuth2最主要的擴展就是提供了ID Token
。ID Token
是一個安全令牌
,是一個授權服務器
提供的包含用戶信息(由一組Cliams構成以及其他輔助的Cliams)的JWT
格式的數(shù)據(jù)結構。
另外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 }
如何獲取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。
Resource Owner Password Credentials Grant是需要用途提供賬號密碼給RP的,賬號密碼給到RP了,就不再需要ID Token了。
Client Credentials Grant這種方式根本就不需要用戶參與,更談不上用戶身份認證了。這也能反映授權和認證的差異,以及只使用OAuth2來做身份認證的事情是遠遠不夠的,也是不合適的。
這里主要介紹基于Authorization Code的認證請求/答復。
基于Authorization Code的認證請求的請求
所有的Token
都是通過Token EndPoint
來發(fā)放的。構建一個OIDC的Authentication Request需要提供如下的參數(shù):
- scope:必須。OIDC的請求必須包含值為
openid
的scope的參數(shù)。 - response_type:必選。同OAuth2。
- client_id:必選。同OAuth2。
- redirect_uri:必選。同OAuth2。
- state:推薦。同OAuth2。防止CSRF, XSRF。
以上這5個參數(shù)是和OAuth2相同的。除此之外,還定義了一些參數(shù)【2】例如:
- response_mode:可選。OIDC新定義的參數(shù),用來指定Authorization Endpoint以何種方式返回數(shù)據(jù)。
一個簡單的示例如下:
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
基于Authorization Code的認證請求的響應
在授權服務器
接收到認證請求之后,需要對請求參數(shù)做嚴格的驗證。驗證通過后引導EU
進行身份認證并且同意授權。在這一切都完成后,會重定向到RP
指定的回調地址,并且把code
和state
參數(shù)傳遞過去。比如:
HTTP/1.1 302 Found
Location: https://client.example.org/cb?
code=SplxlOBeZQQYbYS6WxSbIA
&state=af0ifjsldkj
獲取ID Token
RP
使用上一步獲得的code
來請求Token EndPoint
,這一步同OAuth2。然后Token EndPoint
會返回響應的Token
,其中除了OAuth2規(guī)定的部分數(shù)據(jù)外,還會附加一個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
進行驗證。至此,可以說用戶身份認證就可以完成了,后續(xù)可以根據(jù)UserInfo EndPoint
獲取更完整的信息。
OIDC協(xié)議簇
除了Core核心規(guī)范內容多一點之外,另外7個都是很簡單且簡短的規(guī)范,另外Core是基于OAuth2的,也就是說其中很多東西在復用OAuth2。
OpenId Connect定義了一個發(fā)現(xiàn)協(xié)議,它允許Client輕松的獲取有關如何和特定的身份認證提供者進行交互的信息。在另一方面,還定義了一個Client注冊協(xié)議,允許Client引入新的身份提供程序(identity providers)。通過這兩種機制和一個通用的身份API,OpenId Connect可以運行在互聯(lián)網(wǎng)規(guī)模上運行良好,在那里沒有任何一方事先知道對方的存在。
UserInfo Endpoint
UserIndo EndPoint
是一個受OAuth2保護的資源。在RP
得到Access Token
后可以請求此資源,然后獲得一組EU
相關的Claims
,這些信息可以說是ID Token
的擴展,
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"
}
【1】OAuth2中的access_token
【2】[認證 & 授權] 4. OIDC(OpenId Connect)身份認證(核心部分)