OIDC和OAuth2协议

OIDC,全称为OpenID Connect,是基于OAuth 2.0 的身份认证协议。在理解OIDC之前,需要先搞清楚OAuth 2.0的相关定义。

认证和授权

首先,我们需要区分Authentication和Authorization,即认证和授权。

  • Authentication(认证),指的是身份验证,验证“你是谁”的过程。

  • Authorization(授权),指的是权限授予,验证“你能访问什么”的过程。

举个生活中的例子,当我们需要登机时,需要出示登机牌和机票。登机牌证明了我们的身份,机票证明了我们可以乘坐什么舱位。在实际的使用场景中,Authentication和Authorization是相互依赖的,了解了这两个概念,有助于我们了解OIDC的相关概念。

OAuth2.0是一个授权协议,并不能解决身份认证的问题。 OIDC才是认证协议,真正完成了单点登录。

密码和令牌

假设如下场景:

  1. 当用户张三需要访问他的邮箱系统的时候,邮箱系统需要校验张三的用户名和密码。

  2. 假如说另一个业务系统OA也想代替张三来代收邮件。

那么问题就来了,在OAuth2出现之前,通常的做法就是张三把他的密码share给这个OA系统。 这么做存在严重的安全隐患:

  1. 如何控制OA系统的访问权限,如何不让OA系统以张三的名义随便发邮件?

  2. 如何回收OA系统访问邮件的权限?

OAuth2就是为了解决这个问题而诞生的,OAuth2引入了”授权“以及access_token的概念。

令牌(token)和密码(password)的作用都能够让OA系统访问邮箱系统,但他们有以下几点差异:

  1. token是短期的,到期自动失效,用户自己无法修改。密码一般长期有效,用户不修改,就不会发生变化。

  2. 令牌可以被数据所有者撤销,会立即失效。

  3. 令牌有权限范围(scope),比如只能接收邮件,不能发送邮件。对于网络服务来说,只读令牌就比读写令牌更安全。密码一般是完整权限。

OAuth2.0的授权类型

根据 RFC 6749,OAuth 2.0 对于如何颁发令牌的细节,规定得非常详细,分成以下几种授权类型(authorization grant)。

授权码模式(Authorization Code)

  1. 用户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参数表示要求的授权范围

  1. 用户跳转后,IDP网站会要求用户登录,然后询问是否同意给予SP网站授权。用户表示同意之后,这时IDP网站就会跳回redirect_uri参数指定的网址。跳转时,会传回一个授权码。
https://www.sp.com/callback?code=AUTHORIZATION_CODE
  1. 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
}'
  1. 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
}