10 分钟理解什么是 OpenID Connect(OIDC) 协议

什么是 OIDC

OIDC是一个OAuth2上层的简单身份层协议。它允许客户端验证用户的身份并获取基本的用户配置信息。OIDC使用JSON Web Token(JWT)作为信息返回,通过符合OAuth2的流程来获取,更多详细 10 分钟理解什么是 OAuth 2.0 协议

OAuth2与资源访问和共享有关,而OIDC与用户身份验证有关。

其目的是为您提供多个站点的登录名。每次需要使用OIDC登录网站时,都会被重定向到登录的OpenID网站,然后再回到该网站。例如,如果选择使用Google帐户登录Auth0,这就使用了OIDC。成功通过Google身份验证并授权Auth0访问您的信息后,Google会将有关用户和执行的身份验证的信息发送回Auth0。此信息在JWT中返回,包含ID Token或者Access Token。

JWT包含Claims,它们是有关实体(通常是用户)的Claims(例如名称或电子邮件地址)和其他元数据。OIDC规范定义了一组标准的权利要求。这组标准声明包括姓名,电子邮件,性别,出生日期等。但是,如果要获取有关用户的信息,并且当前没有最能反映此信息的标准声明,则可以创建自定义声明并将其添加到令牌中。

较OAuth2,OIDC有一些不同的概念:

  • OpenID Provider(OP),实现OIDC的OAuth2授权服务器
  • Relying Party(RP),使用OIDC的OAuth2客户端
  • End-User(EU),用户
  • ID Token,JWT格式的授权Claims
  • UserInfo Endpoint,用户信息接口,通过ID Token访问时返回用户信息,此端点必须为HTTPS

协议流程

从理论上来讲,OIDC协议遵循以下步骤:

  1. RP发送认证请求到OP
  2. OP验证End-User并颁发授权
  3. OP用ID Token(通常是Access Token)进行响应
  4. RP携带Access Token发送请求到UserInfo Endpoint
  5. UserInfo Endpoint返回End-User的Claims
+--------+                                   +--------+
|        |                                   |        |
|        |---------(1) AuthN Request-------->|        |
|        |                                   |        |
|        |  +--------+                       |        |
|        |  |        |                       |        |
|        |  |  End-  |<--(2) AuthN & AuthZ-->|        |
|        |  |  User  |                       |        |
|   RP   |  |        |                       |   OP   |
|        |  +--------+                       |        |
|        |                                   |        |
|        |<--------(3) AuthN Response--------|        |
|        |                                   |        |
|        |---------(4) UserInfo Request----->|        |
|        |                                   |        |
|        |<--------(5) UserInfo Response-----|        |
|        |                                   |        |
+--------+                                   +--------+

ID Token

这里预先解释ID Token的含义,OIDC对OAuth2进行的主要扩展(用户用户身份验证)就是ID Token,为JWT格式。其中包含授权服务器对用户验证的Claims和其它请求的Claims。

在ID Token中,以下Clams适用于使用OIDC的所有OAuth2:

  • iss,必须,发行机构Issuer,大小写敏感的URL,不能包含query参数
  • sub,必须,用户身份Subject,Issuer为End-User分配的唯一标识符,大小写敏感不超过255 ASCII自符
  • aud,必须,特别的身份Audience,必须包含OAuth2的client_id,大小写敏感的字符串/数组
  • exp,必须,iat到期时间Expire,参数要求当前时间在该时间之前,通常可以时钟偏差几分钟,unix时间戳
  • iat,必须,JWT颁发时间Issuer at time,unix时间戳
  • auth_time,End-User验证时间,unix时间戳。当发出max_age或auth_time Claims时,必须。
  • nonce,用于将Client session和ID Token关联,减轻重放攻击,大小写敏感字符串
  • acr,可选,Authentication Context Class Reference,0 End-User不符合ISO/IEC 28115 level 1,不应该授权对任何货币价值的资源访问。大小写敏感的字符串。
  • amr,可选,Authentication Methods References,JSON字符串数组,身份验证的表示符,如可能使用了密码和OTP身份验证方式
  • azp,可选,Authorized party,被授权方。如果存在必须包含OAuth2的Client ID,仅当ID Token有单个Audience且与授权方不同时,才需要此Claim

ID Token可能包含其它Claims,任何未知的Claims都必须忽略。ID Token必须使用JWS进行签名,并分别使用JWS和JWE进行可选的签名和加密,从而提供身份验证、完整性、不可抵赖性和可选的机密性。如果对ID Token进行了加密,则必须先对其签名,结果是一个嵌套的JWT。ID Token不能使用nonce作为alg值,除非所使用的响应类型没有从Authorization Endpoint返回任何ID Token(如Authorization Code Flow),并且客户端在注册时显示请求使用nonce。

授权

身份验证遵循以下三种方式;授权码方式(response_type=code)、隐式方式(response_type=id_token token或response_type=id_token)、混合方式。

下表是三种方式的特征:

属性 授权码 隐式 混合
Token从authorization端点返回 no yes no
Token从token端点返回 yes no no
Token未显示给浏览器 yes no no
能够验证客户端 yes no yes
可以刷新Token yes no yes
一次交流 no yes no
服务器到服务器 yes no no

response_type对应的身份验证方式:

response_type 方式
code 授权码
id_token 隐式
id_token token 隐式
code id_token 混合
code token 混合
code id_token token 混合

除了由OAuth2定义的“response_type”之外,所有code均在 OAuth2多种响应类型编码实践。

注意OAuth2为隐式类型定义token的响应类型,但OIDC不会使用此响应类型,因为不会返回ID Token。

授权码方式

使用授权码方式时,所有Token从Token端点返回。授权码将授权code返回给客户端,然后客户端可以将其直接交换为ID Token和Access Token。这样的好处是不会向User-Agent及可能访问User-Agent的其它恶意应用公开任何Token。授权服务器还可以在交换Access Token的授权code之前对客户端进行身份验证。授权code适用于可以安全的维护其自身和授权服务器之间的客户端机密的客户端。

执行以下步骤:

  1. 客户端(RP)准备一个包含所需请求参数的身份验证请求
  2. 客户端将(RP)请求发送到授权服务器(OP)
  3. 授权服务器(OP)对用户(EU)进行身份验证
  4. 授权服务器(OP)获得用户同意/或授权
  5. 授权服务器(OP)使用授权码将用户发送回客户端(RP)
  6. 客户端(RP)使用Token Endpoint的授权码来请求响应
  7. 客户端(RP)收到响应,该响应Body中包含ID Token和Access Token
  8. 客户端(RP)验证ID Token并检索用户的标识符

授权请求

授权服务器(OP)的authorization端点需要支持GET和POST方法,GET采用Query String序列化,POST采用Form序列化。OIDC采用OAuth2的授权码流程参数:

  • scope,必须,OIDC必须包含openid的scope参数
  • response_type,必须,同OAuth2
  • client_id,必须,同OAuth2
  • redirect_uri,必须,同OAuth2
  • state,可选,同OAuth2

如:

HTTP/1.1 302 Found
Location: https://openid.c2id.com/login?
          response_type=code
          &scope=openid
          &client_id=s6BhdRkqt3
          &state=af0ifjsldkj
          &redirect_uri=https%3A%2F%2Fclient.example.org%2Fcb

授权响应

OP收到验证请求后,需要对请求参数做严格的验证:

  1. 验证OAuth2的相关参数
  2. 验证scope是否有openid参数,如果没有则为OAuth2请求
  3. 验证所有必须的参数是否都存在
  4. 如果sub是被要求了,必须尽在由子值标识的最终用户与活动session通过身份验证的情况下积极响应。不得使用不用用户的ID Token或Access Token响应,即使这些用户与授权服务器由活动session。如果支持claims,则可以使用id_token_hint发出请求。

验证通过后引导EU进行身份认证并同意授权。完成后,会重定向到RP指定的回调地址,并携带code和state相关参数:

HTTP/1.1 302 Found
Location: https://client.example.org/cb?
          code=SplxlOBeZQQYbYS6WxSbIA
          &state=af0ifjsldkj

获取Token

RP使用上一步获得的code请求token端点,然后就可以获得响应Token,其中除了OAuth2规定的数据外,还会附加一个id_token的字段,如:

POST /token HTTP/1.1
Host: openid.c2id.com
Content-Type: application/x-www-form-urlencoded
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW

grant_type=authorization_code
 &code=SplxlOBeZQQYbYS6WxSbIA
 &redirect_uri=https%3A%2F%2Fclient.example.org%2Fcb

成功后,OP会返回带有ID Token的JSON数据:

  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"
  }

在拿到这些信息后,需要对id_token及access_token进行验证。验证成功就可以通过UserInfo端点获取用户信息了。

验证Token

授权服务器必须验证Token的有效性:

  • 根据RFC6749
  • 验证ID Token规则
  • 验证Access Token规则

UserInfo

获取用户信息,客户端(RP)可以通过GET或POST请求通过UserInfo Endpoint获取用户信息。

GET /userinfo HTTP/1.1
Host: openid.c2id.com
Authorization: Bearer SlAV32hkKG

请求成功:

{
   "sub"                     : "alice",
   "email"                   : "alice@wonderland.net",
   "email_verified"          : true,
   "name"                    : "Alice Adams",
   "given_name"              : "Alice",
   "family_name"             : "Adams",
   "phone_number"            : "+359 (99) 100200305",
   "profile"                 : "https://c2id.com/users/alice",
   "https://c2id.com/groups" : [ "audit", "admin" ]
}

隐式授权

隐式授权,所有Token都从授权端点返回。主要由浏览器中使用脚本语言实现的客户机使用。访问Token和ID Token直接返回给客户端,授权服务器不执行客户端身份验证。

  1. 客户端(RP)携带认证参数发送请求到授权服务器(OP)
  2. 授权服务器(OP)验证用户并得到用户批准
  3. 授权服务器(OP)携带用户相关信息+ID Token/Access Token返回到客户端(RP)
  4. 客户端(RP)验证ID Token和检索用户标识符

授权请求

  • response_type,必须,’id_token token’或’id_token’。无Access Token使用’id_token’
  • redirect_uri,必须,OP处登记的重定向地址
  • nonce,必须,隐式授权必须

授权响应

  • access_token,如果response_type是id_token可以不反回
  • token_type,固定为Bearer,
  • id_token,必须,ID Token
  • state
  • expires_in,可选,Access Token到期时间(s)

之后就可以拿着ID Token

混合授权

是上面两种模式的混合。可选response_type有:code id_token,code token,code id_token token。

参考资料

[1] https://openid.net/specs/openid-connect-core-1_0.html
[2] https://www.jianshu.com/p/be7cc032a4e9
[3] https://demo.c2id.com/oidc-client/

本文链接:参与评论 »

--EOF--

Comments