[认证受权] 4.OIDC(OpenId Connect)身份认证受权(核心部分)

原文: [认证受权] 4.OIDC(OpenId Connect)身份认证受权(核心部分)

1 什么是OIDC?

看一下官方的介绍(http://openid.net/connect/):html

OpenID Connect 1.0 is a simple identity layer on top of the OAuth 2.0 protocol. It allows Clients to verify the identity of the End-User based on the authentication performed by an Authorization Server, as well as to obtain basic profile information about the End-User in an interoperable and REST-like manner.前端

OpenID Connect allows clients of all types, including Web-based, mobile, and JavaScript clients, to request and receive information about authenticated sessions and end-users. The specification suite is extensible, allowing participants to use optional features such as encryption of identity data, discovery of OpenID Providers, and session management, when it makes sense for them.git

简单来讲:OIDC是OpenID Connect的简称,OIDC=(Identity, Authentication) + OAuth 2.0。它在OAuth2上构建了一个身份层,是一个基于OAuth2协议的身份认证标准协议。咱们都知道OAuth2是一个受权协议,它没法提供完善的身份认证功能(关于这一点请参考[认证受权] 3.基于OAuth2的认证(译)),OIDC使用OAuth2的受权服务器来为第三方客户端提供用户的身份认证,并把对应的身份认证信息传递给客户端,且能够适用于各类类型的客户端(好比服务端应用,移动APP,JS应用),且彻底兼容OAuth2,也就是说你搭建了一个OIDC的服务后,也能够看成一个OAuth2的服务来用。应用场景如图:github

OIDC已经有不少的企业在使用,好比Google的帐号认证受权体系Microsoft的帐号体系也部署了OIDC,固然这些企业有的也是OIDC背后的推进者。除了这些以外,有不少各个语言版本的开源服务端组件,客户端组件等等(http://openid.net/developers/certified/);web

理解OIDC的前提是须要理解OAuth2,这里假设你们都有OAuth2的基础,不清楚的能够先阅读本系列的前几篇OAuth2的文章。json

2 OIDC 协议族

OIDC自己是有多个规范构成,其中包含一个核心的规范,多个可选支持的规范来提供扩展支持,简单的来看一下:后端

  1. Core:必选。定义OIDC的核心功能,在OAuth 2.0之上构建身份认证,以及如何使用Claims来传递用户的信息。
  2. Discovery:可选。发现服务,使客户端能够动态的获取OIDC服务相关的元数据描述信息(好比支持那些规范,接口地址是什么等等)。
  3. Dynamic Registration :可选。动态注册服务,使客户端能够动态的注册到OIDC的OP(这个缩写后面会解释)。
  4. OAuth 2.0 Multiple Response Types :可选。针对OAuth2的扩展,提供几个新的response_type。
  5. OAuth 2.0 Form Post Response Mode:可选。针对OAuth2的扩展,OAuth2回传信息给客户端是经过URL的querystring和fragment这两种方式,这个扩展标准提供了一基于form表单的形式把数据post给客户端的机制。
  6. Session Management :可选。Session管理,用于规范OIDC服务如何管理Session信息。
  7. Front-Channel Logout:可选。基于前端的注销机制,使得RP(这个缩写后面会解释)能够不使用OP的iframe来退出。
  8. Back-Channel Logout:可选。基于后端的注销机制,定义了RP和OP直接如何通讯来完成注销。

除了上面这8个以外,还有其余的正在制定中的扩展。看起来是挺多的,不要被吓到,其实并非很复杂,除了Core核心规范内容多一点以外,另外7个都是很简单且简短的规范,另外Core是基于OAuth2的,也就是说其中不少东西在复用OAuth2,因此说你理解了OAuth2以后,OIDC就是很是容易理解的了,咱们这里就只关注OIDC引入了哪些新的东西(Core,其他7个可选规范不作介绍,可是可能会说起到)。千言万语都不如一张图,没图你说个***。安全

上图是官方给出的一个OIDC组成结构图,咱们暂时只关注Core的部分,其余的部分了解是什么东西就能够了,看成黑盒来用。就像当初的AJAX同样,它其实并非一个新的技术,而是结合不少已有的技术,按照规范的方式组合起来,就是AJAX。同理,OIDC也不是新技术,它主要是借鉴OpenId的身份标识,OAuth2的受权和JWT包装数据的方式,把这些技术融合在一块儿就是OIDC。服务器

3 OIDC 核心概念

OAuth2提供了Access Token来解决受权第三方客户端访问受保护资源的问题;OIDC在这个基础上提供了ID Token来解决第三方客户端标识用户身份认证的问题。OIDC的核心在于在OAuth2的受权流程中,一并提供用户的身份认证信息(ID Token)给到第三方客户端,ID Token使用JWT格式来包装,得益于JWT(JSON Web Token)的自包含性,紧凑性以及防篡改机制,使得ID Token能够安全的传递给第三方客户端程序而且容易被验证。此外还提供了UserInfo的接口,用户获取用户的更完整的信息。session

3.1 OIDC 主要术语

主要的术语以及概念介绍(完整术语参见http://openid.net/specs/openid-connect-core-1_0.html#Terminology):

  1. EU:End User:一我的类用户。
  2. RP:Relying Party ,用来代指OAuth2中的受信任的客户端,身份认证和受权信息的消费方;
  3. OP:OpenID Provider,有能力提供EU认证的服务(好比OAuth2中的受权服务),用来为RP提供EU的身份认证信息;
  4. ID Token:JWT格式的数据,包含EU身份认证的信息。
  5. UserInfo Endpoint:用户信息接口(受OAuth2保护),当RP使用Access Token访问时,返回受权用户的信息,此接口必须使用HTTPS。

3.2 OIDC 工做流程

从抽象的角度来看,OIDC的流程由如下5个步骤构成:

  1. RP发送一个认证请求给OP;
  2. OP对EU进行身份认证,而后提供受权;
  3. OP把ID Token和Access Token(须要的话)返回给RP;
  4. RP使用Access Token发送一个请求UserInfo EndPoint;
  5. UserInfo EndPoint返回EU的Claims。

上图取自Core规范文档,其中AuthN=Authentication,表示认证;AuthZ=Authorization,表明受权。注意这里面RP发往OP的请求,是属于Authentication类型的请求,虽然在OIDC中是复用OAuth2的Authorization请求通道,可是用途是不同的,且OIDC的AuthN请求中scope参数必需要有一个值为的openid的参数(后面会详细介绍AuthN请求所需的参数),用来区分这是一个OIDC的Authentication请求,而不是OAuth2的Authorization请求。

3.3 ID Token

上面提到过OIDC对OAuth2最主要的扩展就是提供了ID Token。ID Token是一个安全令牌,是一个受权服务器提供的包含用户信息(由一组Cliams构成以及其余辅助的Cliams)的JWT格式的数据结构。ID Token的主要构成部分以下(使用OAuth2流程的OIDC)。

  1. iss = Issuer Identifier:必须。提供认证信息者的惟一标识。通常是一个https的url(不包含querystring和fragment部分)。
  2. sub = Subject Identifier:必须。iss提供的EU的标识,在iss范围内惟一。它会被RP用来标识惟一的用户。最长为255个ASCII个字符。
  3. aud = Audience(s):必须。标识ID Token的受众。必须包含OAuth2的client_id。
  4. exp = Expiration time:必须。过时时间,超过此时间的ID Token会做废再也不被验证经过。
  5. iat = Issued At Time:必须。JWT的构建的时间。
  6. auth_time = AuthenticationTime:EU完成认证的时间。若是RP发送AuthN请求的时候携带max_age的参数,则此Claim是必须的。
  7. nonce:RP发送请求的时候提供的随机字符串,用来减缓重放攻击,也能够来关联ID Token和RP自己的Session信息。
  8. acr = Authentication Context Class Reference:可选。表示一个认证上下文引用值,能够用来标识认证上下文类。
  9. amr = Authentication Methods References:可选。表示一组认证方法。
  10. azp = Authorized party:可选。结合aud使用。只有在被认证的一方和受众(aud)不一致时才使用此值,通常状况下不多使用。

ID Token一般状况下还会包含其余的Claims(毕竟上述claim中只有sub是和EU相关的,这在通常状况下是不够的,必须还须要EU的用户名,头像等其余的资料,OIDC提供了一组公共的cliams,请移步这里http://openid.net/specs/openid-connect-core-1_0.html#StandardClaims)。另外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   }

3.4 认证

解释完了ID Token是什么,下面就看一下OIDC如何获取到ID Token,由于OIDC基于OAuth2,因此OIDC的认证流程主要是由OAuth2的几种受权流程延伸而来的,有如下3种:

  1. Authorization Code Flow:使用OAuth2的受权码来换取Id Token和Access Token。
  2. Implicit Flow:使用OAuth2的Implicit流程获取Id Token和Access Token。
  3. Hybrid Flow:混合Authorization Code Flow+Implici Flow。

这里有个小问题你们能够思考下,OAuth2中还有基于Resource Owner Password Credentials Grant和Client Credentials Grant的方式来获取Access Token,为何OIDC没有扩展这些方式呢?

Resource Owner Password Credentials Grant是须要用途提供帐号密码给RP的,帐号密码给到RP了,还要什么自行车(ID Token)。。。

Client Credentials Grant这种方式根本就不须要用户参与,更谈不上用户身份认证了。这也能反映受权和认证的差别,以及只使用OAuth2来作身份认证的事情是远远不够的,也是不合适的。

3.4.1 基于Authorization Code的认证请求

这种方式使用OAuth2的Authorization Code的方式来完成用户身份认证,全部的Token都是经过Token EndPoint(OAuth2中定义:https://tools.ietf.org/html/rfc6749#section-3.2)来发放的。构建一个OIDC的Authentication Request须要提供以下的参数:

  1. scope:必须。OIDC的请求必须包含值为“openid”的scope的参数。
  2. response_type:必选。同OAuth2。
  3. client_id:必选。同OAuth2。
  4. redirect_uri:必选。同OAuth2。
  5. state:推荐。同OAuth2。防止CSRF, XSRF。

以上这5个参数是和OAuth2相同的。除此以外,还定义了以下的参数:

  1. response_mode:可选。OIDC新定义的参数(OAuth 2.0 Form Post Response Mode),用来指定Authorization Endpoint以何种方式返回数据。
  2. nonce:可选。ID Token中的出现的nonce就是来源于此。
  3. display : 可选。指示受权服务器呈现怎样的界面给EU。有效值有(page,popup,touch,wap),其中默认是page。page=普通的页面,popup=弹出框,touch=支持触控的页面,wap=移动端页面。
  4. prompt:可选。这个参数容许传递多个值,使用空格分隔。用来指示受权服务器是否引导EU从新认证和赞成受权(consent,就是EU完成身份认证后的确认赞成受权的页面)。有效值有(none,login,consent,select_account)。none=不实现现任何认证和确认赞成受权的页面,若是没有认证受权过,则返回错误login_required或interaction_required。login=从新引导EU进行身份认证,即便已经登陆。consent=从新引导EU确认赞成受权。select_account=假如EU在受权服务器有多个帐号的话,容许EU选择一个帐号进行认证。
  5. max_age:可选。表明EU认证信息的有效时间,对应ID Token中auth_time的claim。好比设定是20分钟,则超过了时间,则须要引导EU从新认证。
  6. ui_locales:可选。用户界面的本地化语言设置项。
  7. id_token_hint:可选。以前发放的ID Token,若是ID Token通过验证且是有效的,则须要返回一个正常的响应;若是有误,则返回对应的错误提示。
  8. login_hint:可选。向受权服务器提示登陆标识符,EU可能会使用它登陆(若是须要的话)。好比指定使用用户使用blackheart帐号登陆,固然EU也可使用其余帐号登陆,这只是相似html中input元素的placeholder。
  9. acr_values:可选。Authentication Context Class Reference values,对应ID Token中的acr的Claim。此参数容许多个值出现,使用空格分割。

以上是基于Authorization Code方式的OIDC的认证请求所需的参数。在OIDC的其余认证流程中也会有其余的参数或不一样的参数值(稍有差别)。一个简单的示例以下:

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

也能够是一个基于302的重定向方式。

3.4.2 基于Authorization Code的认证请求的响应

在受权服务器接收到认证请求以后,须要对请求参数作严格的验证,具体的规则参见http://openid.net/specs/openid-connect-core-1_0.html#AuthRequestValidation,验证经过后引导EU进行身份认证而且赞成受权。在这一切都完成后,会重定向到RP指定的回调地址,而且把code和state参数传递过去。好比:

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

3.4.3 获取ID Token

RP使用上一步得到的code来请求Token EndPoint,这一步同OAuth2,就再也不展开细说了。而后Token EndPoint会返回响应的Token,其中除了OAuth2规定的部分数据外,还会附加一个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进行验证(具体的规则参见http://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidationhttp://openid.net/specs/openid-connect-core-1_0.html#ImplicitTokenValidation)。至此,能够说用户身份认证就能够完成了,后续能够根据UserInfo EndPoint获取更完整的信息。

3.4.4 Implicit Flow和Hybrid Flow

Implicit Flow的工做方式是在OAuth2 Implicit Flow上附加提供id_token,固然,认证请求的参数和基于Authorization Code的流程稍有不一样,具体的差别参见http://openid.net/specs/openid-connect-core-1_0.html#ImplicitAuthRequest,这里就不作详细介绍了。

Hybrid Flow则=Authorization Code Flow+Implicit Flow,也再也不详细介绍了。

3.5 UserInfo Endpoint

UserIndo EndPoint是一个受OAuth2保护的资源。在RP获得Access Token后能够请求此资源,而后得到一组EU相关的Claims,这些信息能够说是ID Token的扩展,好比若是你以为ID Token中只需包含EU的惟一标识sub便可(避免ID Token过于庞大),而后经过此接口获取完整的EU的信息。此资源必须部署在TLS之上,例如:

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

其中sub表明EU的惟一标识,这个claim是必须的,其余的都是可选的。

4 总结

继OAuth2以后,感受OIDC也要大放异彩了。其自己是一个彻底开放的标准,并且兼容众多的已有的IDP(身份提供商),好比基于SAML的、基于WS-Federation的等等已有的身份认证系统,均可以做为OIDC的OP存在。总结一下OIDC有那些特性和好处吧:

  1. OIDC使得身份认证能够做为一个服务存在。
  2. OIDC能够很方便的实现SSO(跨顶级域)。
  3. OIDC兼容OAuth2,可使用Access Token控制受保护的API资源。
  4. OIDC能够兼容众多的IDP做为OIDC的OP来使用。
  5. OIDC的一些敏感接口均强制要求TLS,除此以外,得益于JWT,JWS,JWE家族的安全机制,使得一些敏感信息能够进行数字签名、加密和验证,进一步确保整个认证过程当中的安全保障。

以上内容均是我的的一些理解,若是错误之处,欢迎指正!

5 Example

笔者基于IdentityServer3和IdentitySever4(二者都是基于OIDC的一个.NET版本的开源实现)写的一个集成SSO,API访问受权控制,QQ联合登录(做为OP)的demo:https://github.com/linianhui/oidc.example 。

6 参考

官方资料:

http://openid.net/connect/

http://openid.net/connect/faq/

http://openid.net/developers/certified/

JWT : https://tools.ietf.org/html/rfc7519

JWS:https://tools.ietf.org/html/rfc7515

JWE:https://tools.ietf.org/html/rfc7516

.NET的开源实现:https://github.com/IdentityServer

视频:Identity, Authentication + OAuth = OpenID Connect

案例:

https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-protocols-openid-connect-code

https://developers.google.com/identity/protocols/OpenIDConnect

相关文章
相关标签/搜索