OIDC和OAuth2协议
OIDC,全称为OpenID Connect,是基于OAuth 2.0 的身份认证协议。在理解OIDC之前,需要先搞清楚OAuth 2.0的相关定义。
认证和授权
首先,我们需要区分Authentication和Authorization,即认证和授权。
-
Authentication(认证),指的是身份验证,验证“你是谁”的过程。
-
Authorization(授权),指的是权限授予,验证“你能访问什么”的过程。
举个生活中的例子,当我们需要登机时,需要出示登机牌和机票。登机牌证明了我们的身份,机票证明了我们可以乘坐什么舱位。在实际的使用场景中,Authentication和Authorization是相互依赖的,了解了这两个概念,有助于我们了解OIDC的相关概念。
OAuth2.0是一个授权协议,并不能解决身份认证的问题。 OIDC才是认证协议,真正完成了单点登录。
密码和令牌
假设如下场景:
-
当用户张三需要访问他的邮箱系统的时候,邮箱系统需要校验张三的用户名和密码。
-
假如说另一个业务系统OA也想代替张三来代收邮件。
那么问题就来了,在OAuth2出现之前,通常的做法就是张三把他的密码share给这个OA系统。 这么做存在严重的安全隐患:
-
如何控制OA系统的访问权限,如何不让OA系统以张三的名义随便发邮件?
-
如何回收OA系统访问邮件的权限?
OAuth2就是为了解决这个问题而诞生的,OAuth2引入了”授权“以及access_token的概念。
令牌(token)和密码(password)的作用都能够让OA系统访问邮箱系统,但他们有以下几点差异:
-
token是短期的,到期自动失效,用户自己无法修改。密码一般长期有效,用户不修改,就不会发生变化。
-
令牌可以被数据所有者撤销,会立即失效。
-
令牌有权限范围(scope),比如只能接收邮件,不能发送邮件。对于网络服务来说,只读令牌就比读写令牌更安全。密码一般是完整权限。
OAuth2.0的授权类型
根据 RFC 6749,OAuth 2.0 对于如何颁发令牌的细节,规定得非常详细,分成以下几种授权类型(authorization grant)。
授权码模式(Authorization Code)
- 用户SP网站点击后就会跳转到IDP网站,授权用户数据给SP网站使用。下面就是SP网站跳转IDP网站的一个示意链接。
https://www.idp.com/oauth/authorize?response_type=code&client_id=CLIENT_ID&redirect_uri=CALLBACK_URL&scope=read
-
response_type参数表示要求返回授权码(code)
-
client_id参数让认证源知道是哪个系统在请求用户信息
-
redirect_uri参数是IDP网站接受或拒绝请求后的跳转网址
-
scope参数表示要求的授权范围
- 用户跳转后,IDP网站会要求用户登录,然后询问是否同意给予SP网站授权。用户表示同意之后,这时IDP网站就会跳回redirect_uri参数指定的网址。跳转时,会传回一个授权码。
https://www.sp.com/callback?code=AUTHORIZATION_CODE
- SP网站拿到授权码以后,在服务端向IDP网站请求令牌token。
这里一定要由服务端发起请求,CLIENT_SECRET不能由前端页面获知
curl \
-X POST https://www.idp.com/oauth/token \
-H 'Content-Type:application/x-www-form-urlencoded' \
-H 'Authorization:Basic {CLIENT_ID: CLIENT_SECRET}' \
-d '{grant_type=authorization_code&
code={code}&
redirect_uri=https://www.sp.com/callback
client_id=CLIENT_ID&
client_secret=CLIENT_SECRET&
grant_type=authorization_code&
code=AUTHORIZATION_CODE&
redirect_uri=CALLBACK_URL
}'
- IDP服务端验证成功后,会返回如下response。
HTTP/1.1 200 OK
Content-Type: application/json
{
"access_token": "xxxxxxx",
"refresh_token": "xxxxxxxxx",
"token_type": "Bearer",
"expires_in": 3600
}
OIDC
我们前面提到了认证和授权,注意,OAuth协议只是一个授权协议,不涉及到身份认证。
OIDC在OAuth2.0的基础上,添加了身份认证的一层。
OIDC增加了以下内容
-
token接口增加了id_token的返回:在获取token接口,除了返回access_token和refresh_token,还需要返回id_token,id_token是一个JWT格式的token,里面携带用户信息。
-
增加了user_info接口:使用access_token可以调用user_info接口获取用户信息。
-
定义了well-known接口:即OIDC的描述文件。
id_token
在OIDC的规范里,详细定义了id_token的格式,必须为JWT格式。
OIDC服务端需要提供一个RSA的公钥,供客户端验证JWT的签名。
id_token里携带了用户基本信息,客户端可以直接使用id_token里的信息,也可以使用access_token调用user_info接口获取用户信息
user_info接口
客户端除了使用id_token标识用户以外,还可以额外调用user_info接口,获取用户信息
curl \
-X GET https://www.idp.com/oauth/user_info \
-H 'Content-Type:application/json ' \
-H 'Authorization:Bearer {access_token}' \
此处使用Bearer Token的方式,详见备注2。
OIDC客户端,返回的用户信息的属性是严格规范的
{
"sub": "us-xxxx",
"name": "张三",
"preferred_username": "zhangsan",
"email": "zhangsan@test.com",
"email_verified": true,
"phone_number": "123456789001",
"phone_number_verified": true
}