认证授权:学习OIDC

 

前言

  上一篇文章介绍了OAuth2.0协议的相关内容,知道OAuth2.0是一个授权协议,无法提供完善的身份认证功能。那么什么来解决身份认证功能呢?——OIDC是一个不错的解决方案。接下来进一步来了解OIDC是什么,有什么效果呢?

一、OIDC是什么

  OIDC是OpenID Connect 的简称,以下是OIDC官方的描述内容:

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.

  主要内容:OIDC是基于OAuth2.0协议的身份层,允许客户端基于授权服务器执行的身份验证来验证最终用户的身份,并以可互操作且类似于REST的方式获取有关最终​​用户的基本配置文件信息。并且支持所有客户端类型(如基于Web的客户端,移动客户端和JavaScript客户端)请求并接收有关经过身份验证的会话和最终用户的信息

二、OIDC相关的协议

  OIDC本身是有多个规范构成,其中最主要的是一个核心的规范,多个可选支持的规范来提供扩展支持

   主要包含:

   Core:必选。定义OIDC的核心功能,在OAuth 2.0之上构建身份认证,以及如何使用Claims来传递用户的信息。
   Discovery:可选。发现服务,使客户端可以动态的获取OIDC服务相关的元数据描述信息
   Dynamic Client Registration:可选。动态注册服务,使客户端可以动态的注册到OIDC的OP
   Session Management:可选。Session管理,用于规范OIDC服务如何管理Session信息
   Form Post Response Mode:可选。针对OAuth2的扩展,OAuth2回传信息给客户端是通过URL的querystring和fragment这两种方式,这个扩展标准提供了一基于form表单的形式把数据post给客户端的机制。
   基础协议:
    OAuth2.0 Core:https://tools.ietf.org/html/rfc6749
    OAuth2.0 Bearer:https://tools.ietf.org/html/rfc6750
    OAuth2.0 Assertions:https://tools.ietf.org/html/rfc7521
    OAuth2.0 JWT Profile:https://tools.ietf.org/html/rfc7523
    OAuth2.0 Responses:可选。针对OAuth2的扩展,提供几个新的response_type。
    JWT(JSON Web Token):https://tools.ietf.org/html/rfc7519
    JWS(JSON Web Signature):https://tools.ietf.org/html/rfc7515
    JWE(JSON Web Encryption):https://tools.ietf.org/html/rfc7516
    JWK(JSON Web Key):https://tools.ietf.org/html/rfc7517
    JWA(JSON Web Algorithms):https://tools.ietf.org/html/rfc7518
    WebFinger:https://tools.ietf.org/html/rfc7033

  

 

  上图是官方给出的一个OIDC组成结构图,可以看出OIDC不是什么新技术,它主要是借鉴OpenId的身份标识,OAuth2的授权和JWT包装数据的方式,组合使用这些技术就是现在的OIDC。

  接下来就来学习里面最核心的相关内容(Core核心规范)。

 三、OIDC核心规范

  1、主要术语

    主要的术语以及概念介绍

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

  2、主要流程

    抽象的看主要包含以下步骤: 

    1. RP(客户端)将请求发送到OpenID提供程序(OP)。
    2. OP验证最终用户并获得授权。
    3. OP用ID Token(通常是访问令牌)进行响应。
    4. RP可以将带有访问令牌(Access Token)的请求发送到UserInfo EndPoint。
    5. UserInfo端点返回有关最终用户(EU)的声明(Claims)。 

    

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

   3、ID Token

    ID Token是OpenID Connect对OAuth 2.0进行的主要扩展(用于使最终用户能够通过身份验证),ID Token是一种安全令牌,其中包含有关使用客户端时授权服务器对最终用户的身份验证的声明(Claims)以及可能的其他请求的声明(Claims)的JWT格式数据。

    ID Token的主要构成部分如下:

    

名称 完整名称 是否必须 描述
iss Issuer Identifier 提供认证信息者的唯一标识。一般是一个https的url(不包含querystring和fragment部分)
sub Subject Identifier iss提供的EU的标识,在iss范围内唯一。它会被RP用来标识唯一的用户。最长为255个ASCII个字符。
aud Audience(s) 标识ID Token的受众。必须包含OAuth2的client_id
exp Expiration time 过期时间,超过此时间的ID Token会作废不再被验证通过。
iat Issued At Time JWT的构建的时间
nonce   RP发送请求的时候提供的随机字符串,用来减缓重放攻击,也可以来关联ID Token和RP本身的Session信息。
auth_time AuthenticationTime / EU完成认证的时间。如果RP发送AuthN请求的时候携带max_age的参数,则此Claim是必须的
acr Authentication Context Class Reference 表示一个认证上下文引用值,可以用来标识认证上下文类
amr Authentication Methods References 表示一组认证方法
azp  Authorized party 结合aud使用。只有在被认证的一方和受众(aud)不一致时才使用此值,一般情况下很少使用

   一般情况在ID Token中还会包含其他声明(Claims)内容,如用户姓名、头像等信息。OIDC提供了一组公共的cliams,可以到这里查看。另外ID Token必须使用JWS进行签名和JWE加密,从而提供认证的完整性、不可否认性以及可选的保密性。

   例如:

  {
   "iss": "https://server.example.com",
   "sub": "24400320",
   "aud": "s6BhdRkqt3",
   "nonce": "n-0S6_WzA2Mj",
   "exp": 1311281970,
   "iat": 1311280970,
   "auth_time": 1311280969,
   "acr": "urn:mace:incommon:iap:silver"
  }

  4、认证

   了解了ID Token后,接下来就来看OIDC如何获取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。

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来做身份认证的事情是远远不够的,也是不合适的。

   4.1 授权码流程(Authorization Code Flow)

授权码流程执行以下步骤。 

    1. 客户端准备一个包含所需请求参数的身份验证请求。
    2. 客户端将请求发送到授权服务器。
    3. 授权服务器对最终用户进行身份验证。
    4. 授权服务器获得最终用户同意/授权。
    5. 授权服务器使用授权码将最终用户发送回客户端。
    6. 客户端使用令牌端点上的授权码来请求响应。
    7. 客户端收到响应,该响应在响应主体中包含ID令牌和访问令牌。
    8. 客户端验证ID令牌并检索最终用户的主题标识符。

    4.1.1 认证请求

      该方式使用OAuth2的Authorization Code的方式来完成用户身份认证,所有的Token都是通过Token EndPoint来发放的。构建一个OIDC的Authentication Request需要提供如下的参数:

参数 必须 说明
scope OIDC的请求必须包含值为“openid”的scope的参数
response_type 同OAuth2
client_id 同OAuth2
redirect_uri 同OAuth2
state 推荐 同OAuth2,防止CSRF, XSRF
response_mode OIDC新定义的参数(OAuth 2.0 Form Post Response Mode),用来指定Authorization Endpoint以何种方式返回数据
nonce ID Token中的出现的nonce就是来源于此
display 指示授权服务器呈现怎样的界面给EU。有效值有(page,popup,touch,wap),其中默认是page。page=普通的页面,popup=弹出框,touch=支持触控的页面,wap=移动端页面。
prompt 这个参数允许传递多个值,使用空格分隔。用来指示授权服务器是否引导EU重新认证和同意授权(consent,就是EU完成身份认证后的确认同意授权的页面)。有效值有(none,login,consent,select_account)。none=不实现现任何认证和确认同意授权的页面,如果没有认证授权过,则返回错误login_required或interaction_required。login=重新引导EU进行身份认证,即使已经登录。consent=重新引导EU确认同意授权。select_account=假如EU在授权服务器有多个账号的话,允许EU选择一个账号进行认证。
max_age 代表EU认证信息的有效时间,对应ID Token中auth_time的claim。比如设定是20分钟,则超过了时间,则需要引导EU重新认证
ui_locales 用户界面的本地化语言设置项
id_token_hint 之前发放的ID Token,如果ID Token经过验证且是有效的,则需要返回一个正常的响应;如果有误,则返回对应的错误提示
login_hint 向授权服务器提示登录标识符,EU可能会使用它登录(如果需要的话)。比如指定使用用户使用blackheart账号登录,当然EU也可以使用其他账号登录,这只是类似html中input元素的placeholder。
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

    4.1.2 认证请求的响应

      授权服务器接收到请求后,进行了:OAuth2.0参数验证、scope参数是否包含openid的范围值、授权服务器必须参数是否存等相关验证(详见);验证通过后引导EU进行身份认证并同意授权。然后会重定向到RP指定的回调地址,并且把code和state参数传递过去:如下:

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

     4.1.3 获取IDToken

      接下来PR会根据获取的Code来请求Token EndPoint,和OAuth2.0请求相同;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"
  }

  4.2 隐式流(Implicit Flow)

隐式流遵循以下步骤: 

    1. 客户端准备一个包含所需请求参数的身份验证请求。
    2. 客户端将请求发送到授权服务器。
    3. 授权服务器对最终用户进行身份验证。
    4. 授权服务器获得最终用户同意/授权。
    5. 授权服务器使用ID令牌和访问令牌(如果要求)将最终用户发送回客户端。
    6. 客户端验证ID令牌并检索最终用户的主题标识符。   

   4.2.1 认证请求

     按照4.1.1定义进行身份验证请求,不同的是,这些身份验证请求参数的使用方式如下:

    • response_type值换成id_token token 或 id_token

     示例如下:      

  GET /authorize?
    response_type=id_token%20token
    &client_id=s6BhdRkqt3
    &redirect_uri=https%3A%2F%2Fclient.example.org%2Fcb
    &scope=openid%20profile
    &state=af0ifjsldkj
    &nonce=n-0S6_WzA2Mj HTTP/1.1
  Host: server.example.com

   4.2.1 认证请求响应

     响应结果如下:

  HTTP/1.1 302 Found
  Location: https://client.example.org/cb#
    access_token=SlAV32hkKG
    &token_type=bearer
    &id_token=eyJ0 ... NiJ9.eyJ1c ... I6IjIifX0.DeWt4Qu ... ZXso
    &expires_in=3600
    &state=af0ifjsldkj

  4.3 混合流(Hybrid Flow)

混合流遵循以下步骤: 

    1. 客户端准备一个包含所需请求参数的身份验证请求。
    2. 客户端将请求发送到授权服务器。
    3. 授权服务器对最终用户进行身份验证。
    4. 授权服务器获得最终用户同意/授权。
    5. 授权服务器使用授权码以及一个或多个其他参数(根据响应类型)将最终用户发送回客户端。
    6. 客户端使用令牌端点上的授权码来请求响应。
    7. 客户端收到响应,该响应在响应主体中包含ID令牌和访问令牌。
    8. 客户端验证ID令牌并检索最终用户的主题标识符。

   4.3.1 认证请求

     按照4.1.1定义进行身份验证请求,不同的是,这些身份验证请求参数的使用方式如下:

    • response_type值换成:code id_tokencode token, or code id_token token.
  GET /authorize?
    response_type=code%20id_token
    &client_id=s6BhdRkqt3
    &redirect_uri=https%3A%2F%2Fclient.example.org%2Fcb
    &scope=openid%20profile%20email
    &nonce=n-0S6_WzA2Mj
    &state=af0ifjsldkj HTTP/1.1
  Host: server.example.com

   4.3.1 认证请求响应

    以下是使用“混合流”成功响应

  HTTP/1.1 302 Found
  Location: https://client.example.org/cb#
    code=SplxlOBeZQQYbYS6WxSbIA
    &id_token=eyJ0 ... NiJ9.eyJ1c ... I6IjIifX0.DeWt4Qu ... ZXso
    &state=af0ifjsldkj

总结:

   OIDC的好处:

    • OIDC使得身份认证可以作为一个服务存在

    • OIDC可以很方便的实现SSO(跨顶级域)

    • OIDC兼容OAuth2,可以使用Access Token控制受保护的API资源
    • OIDC可以兼容众多的IDP(身份提供商)作为OIDC的OP来使用

    • OIDC的一些敏感接口均强制要求TLS,除此之外,得益于JWT,JWS,JWE家族的安全机制,使得一些敏感信息可以进行数字签名、加密和验证,进一步确保整个认证过程中的安全保障

参考

官方资料:

http://openid.net/connect/

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

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

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

参考引用:https://www.cnblogs.com/linianhui/p/openid-connect-core.html

   

posted @ 2020-08-30 22:51  chaney1992  阅读(3245)  评论(2编辑  收藏  举报