理解OIDC协议和认证机制

当互联网应用越来越多,每个应用程序都实现了自己的身份存储、认证和授权,用户需要在应用上反复的注册与登录,体验糟糕,用户身份信息无法在多个应用间共享与同步。当使用企业应用时,企业提供了一系列应用,尽管是同一用户操作,但还是不得不切换注册与登录。

是否可以有那么一种方式,我在某个代理网站注册一个账号,如果登录其他网站时,其他网站可以到代理网站去获取信息。只要代理网站足够富有生命力,那么总是可以省略很多乏味的注册登录流程。

这些想法在技术的发展过程中出现了,如WS-Federation、SAML2.0、OpenID、OAuth、OIDC...

OpenID&OAuth发展历史

图片

还有一些修订和扩展,此处就不体现了,主要是想聚焦于OpenID和OAuth是不一样的。

  • OpenID是面向身份认证,对用户身份进行认证判断是否有效。
  • OAuth是面向授权过程,在已知当前用户身份合法后,控制用户能够访问的资源,考虑保护被访问的资源。

OpenID

OpenID是一个去中心化的网上身份认证系统。对于支持OpenID的网站,用户不需要记住像用户名和密码这样的传统认证标记。取而代之的是,他们只需要预先在一个作为OpenID身份提供者(identity provider, IdP)的网站上注册。OpenID是去中心化的,任何网站都可以使用OpenID来作为用户登录的一种方式,任何网站也都可以作为OpenID身份提供者。OpenID既解决了注册问题而又不需要依赖于中心性的网站来确认数字身份。

OpenID给用户提供一个统一网络身份,遵守OpenID网络身份,遵守OpenID协议的应用网站(在OpenId术语里叫Relying Parties,即RP)需要用户登录时,引导用户到第三方OpenID身份提供方(OpenId术语里叫OpenID identity provider,即OP)去认证,认证通过即登录成功。这样就省去了每个应用网站注册、登录的烦恼与乏味。解决了最开始提到的一个问题,各应用需要独立完成注册与登录流程,需要实现与维护独立的用户数据库。

OAuth

OAuth是一个授权协议,解决的是应用可以访问用户在另一应用上的某些授权资源,但此应用不需要知道用户在另一应用上的账户密码,即无需向另一应用透露用户的凭据便可以访问另一应用上受控的资源。

  • 2006年11月,当时布莱恩·库克正在开发Twitter的OpenID实现。与此同时,社交书签网站Ma.gnolia需要一个解决方案允许使用OpenID的成员授权Dashboard访问他们的服务。这样库克、克里斯·梅西纳和来自Ma.gnolia的拉里·哈尔夫(Larry Halff)与戴维·雷科尔顿会面讨论在Twitter和Ma.gnolia API上使用OpenID进行委托授权。但他们讨论得出结论,认为OpenID没有完成API访问委托的开放标准。
  • 2007年4月,成立了OAuth讨论组,这个由实现者组成的小组撰写了一个开放协议的提议草案。来自Google的德维特·克林顿获悉OAuth项目后,表示他有兴趣支持这个工作。
  • 2007年7月,团队起草了最初的规范。随后,Eran Hammer-Lahav加入团队并协调了许多OAuth的稿件,创建了更为正式的规范。
  • 2007年10月,OAuth Core 1.0最后的草案发布了。
  • 2008年11月,在明尼阿波利斯举行的互联网工程任务组第73次会议上,举行了OAuth的BoF讨论将该协议纳入IETF做进一步的规范化工作。这个会议参加的人很多,关于正式地授权在IETF设立一个OAuth工作组这一议题得到了广泛的支持。
  • 2010年4月,OAuth 1.0协议发表为RFC 5849,一个非正式RFC。
  • 2012年10月,OAuth 2.0发布,正式发表为RFC 6749。OAuth 2.0是OAuth协议的下一版本,但不向下兼容OAuth 1.0。OAuth 2.0关注客户端开发者的简易性,同时为Web应用、桌面应用、手机和智能设备提供专门的认证流程。

Open ID Connect

OpenID团队本来希望用户使用OpenID来进行身份认证, 但因为授权过程包括认证过程作为一部分,所以授权意味着经过了认证这一过程。由此,一些用户选择了OAuth身份认证的简易性,并且由于“管理用户凭据的任务可以委托给外部服务”以及“因为新用户注册过程可以省略,使用该服务的障碍变得更低”等优点而迅速占据主导地位,使用OAuth2.0作为身份认证的趋势变得流行。

OpenID团队不得不承认用户更喜欢OAuth2.0,因此,他们在OAuth之上定义了一个新的身份认证规范OpenID Connect(OIDC),这个规范基于OAuth2.0进行了小幅扩展,在OAuth2.0上构建了身份层,使其可用作身份认证协议。

图片

即OpenID Connect (OIDC) 是基于 OAuth 2.0 构建的身份认证协议。

OpenID+OAuth 2.0=OpenID Connect


OIDC主要术语

  • Claim: 声明信息,简要格式展示(如身份证上格式)。
  • Claims Provider: 声明信息提供方(如OAuth2.0中的Authorization Server能够返回Id token,其中携带着声明信息)。
  • End-User(EU): 人类参与者(完成认证活动的人)。
  • ID Token: 包含EU身份信息的声明(Claim),以JWT(Json Web Token)格式进行存储传递。JWT具有自包含性、紧凑性和防篡改机制等特点。使得ID Token可以安全的传递给第三方应用并且容易被验证。
  • OpenID Provider (OP): 提供身份认证的服务方,OAuth2.0中的Authorization Server(授权服务器),用来提供身份认证服务以及返回Claim信息给第三方应用(Relying Party)。
  • Relying Party (RP):受信任的第三方应用(OAuth2.0中的Client),需要EU完成鉴权,并从OP处获得Claim信息的应用。
  • UserInfo Endpoint:用户信息的接口,当RP使用Access token请求该接口时,能够返回当前用户的身份信息。
    图片

OIDC协议簇

OIDC由一系列规范文档组成,包括一个核心文档和多个可选支持的文档来提供扩展支持:

  • Core:必选。定义了OIDC核心的功能,在OAuth 2.0之上构建身份认证,以及使用声明(如身份证上的简要描述)方式来传递用户的信息。
  • Discovery:可选。定义了第三方应用如何动态发现OIDC服务提供方元数据文档(比如提供的服务地址,采用的加密方法,支持的授权类型等等)。
  • Dynamic Registration:可选。定义了第三方应用如何注册到OIDC身份提供方。
  • OAuth 2.0 Multiple Response Types:定义了几种新的为OAuth2.0响应方式(原来常见的授权码code、隐式授权token基础上增加了id_token以及混合code、token和id_token的方式)。
  • OAuth 2.0 Form Post Response Mode:可选。在OAuth2.0的响应参数上扩展了OIDC所需的参数。提供了基于form表单响应信息的模式,使用post请求类型传给第三方应用,响应参数以application/x-www-form-urlencoded格式存储body中。
  • RP-Initiated Logout: 可选。定义了第三方应用如何注销当前登录用户的机制。
  • Session Management:可选。定义了如何管理OIDC上已登录用户的会话数据,以便当OP上的用户注销时,也能够方便在RP上注销。
  • Front-Channel Logout:可选。基于前端的注销机制,使得第三方应用不需要嵌入认证服务方的iframe来注销。
  • Back-Channel Logout:可选。基于后端的注销机制,定义了RP和OP直接如何通信来完成注销。
  • OpenID Connect Federation: 可选。定义了如何在OP和RP间建立可靠的信任的机制。
  • Initiating User Registration via OpenID Connect: 可选。提供一种方式允许在RP指示下,能够在OP上注册用户的机制。
    如下图所示,上部分描述OIDC的协议簇,以及其中最为核心的Core规范。下部分是作为支撑OIDC的其他协议规范。

图片

对于这些协议来讲,可能看着就难受,简要了解个概念即可,实际开发时这些都会逐渐浮出水面。


OIDC&OpenID2.0差异

  • OpenID Connect执行许多与OpenID 2.0相同的任务,但是API更友好且可使用在原生应用和移动应用。
  • OpenID Connect 定义了用于可靠签名和加密的可选机制。
  • OAuth 1.0a和OpenID 2.0的集成需要扩展,而OIDC本身集成了OAuth2.0。

OIDC&OAuth2.0差异

OIDC仍然使用OAuth中的授权服务器将用户身份认证信息以Id token的方式给到第三方应用,第三方应用可以验证用户标识并从中获取用户的基本信息以及通过OAuth2.0的授权流程访问用户的详细信息。如下图所示,对于用户认证部分采用认证协议如WS-Fed、OIDC等,对于资源的访问控制以OAuth2.0协议为主。

图片

OIDC中的Id token和OAuth2.0中核心的Access token各自效力或是关注的部分不同。

  • OAuth2提供了Access Token来解决授权第三方应用访问受保护资源的问题;
  • OIDC遵循OAuth2.0协议流程在这个基础上提供了ID Token来解决第三方应用标识用户身份的问题。
    图片

OIDC抽象流程

OIDC的流程主要由以下5个步骤构成:

  1. 第三方应用(RP)发送认证请求到认证服务方(OP)
  2. 用户在认证页面进行认证与选择授权内容
  3. 认证服务方(OP)对认证请求进行验证,发送Id token及Access token给第三方应用(RP)
  4. 第三方应用可使用Access token请求用户信息或其他授权内的资源
  5. 资源服务对Access token校验并返回用户信息或资源
    图片

流程上,在OAuth2.0中授权码模式和隐式授权模式下,OIDC和OAuth2.0是一样的,但有几个授权模式在某些情况下,在OIDC中就不存在了,当用户无需经过身份认证过程即可获得Access token,比如如下两种模式。

  • 资源所有者密码凭证授权(Resource Owner Password Credentials Grant):第三方应用能够获取到用户的登录凭证就直接可以向授权服务器请求Access token,如此,就去掉了标识用户身份这一过程了。
  • 客户端凭证授权(Client Credentials Grant):这种是标识客户端,直接的参与者人都没有加入到流程中,也不存在标识用户是谁了。

OIDC的几种认证方式

OIDC的认证流程主要是由OAuth2的几种授权流程延伸而来的,主要有以下3种:

  • 授权码流程(Authorization Code Flow):基于OAuth2的授权码流程,在原来code换取Access token的基础上增加了一个Id token。
  • 隐式流程(Implicit Flow):基于OAuth2的隐式流程,在原来从授权服务器重定向到第三方应用仅返回Access token的基础上增加了一个Id token。
  • 混合流程(Hybrid Flow):混合了授权码流程(Authorization Code Flow)和隐式流程(Implici Flow),能够按照参数配置的不同,控制Id token的返回位置与Access token的有无。
    注意:如上省略了Refresh token,关注于Id token和Access token。

授权码流程

1、第三方应用通过浏览器重定向到授权服务器(OP)的认证页面来进行用户认证与执行授权流程。重定向时使用的参数是基于OAuth2.0中的大部分参数。主要如下几个

  • scope:必选。选择的授权范围,当是OIDC请求时,需要包含openid选项,如下示例所示。
  • response_type:必选。响应类型方式,使用授权码流程时,为code.
  • client_id:必选。第三方应用提前在授权服务器处注册得到的id值。
  • redirect_uri:必选。重定向到OP认证授权完毕后重定向到RP的地址。
  • state:推荐。第三方应用(RP)提供的一个字符串,授权服务器(OP)会原样返回给第三方应用(RP),以阻止CSRF攻击。
    如上几个是常使用到的授权码流程中所需的参数,实际上还有许多其他参数可以依照不同场景的需要使用上。其他参数信息https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest

图片

2、当用户完成认证信息填写与执行授权操作后,授权服务器(OP)对用户认证信息的审核和授权范围的校验。有效后重定向到第三方应用(RP)指定的回调地址(第1步中的redirect_uri),将code和state参数作为查询参数附加在回调地址后。

图片
3、第三方应用(RP)通过code来请求认证服务方的Token EndPoint换取Token。

图片

然后Token EndPoint会返回响应的Token,其中除了OAuth2规定的部分数据外,还会附加一个id_token的字段。

{
    "access_token": "SlAV32hkKG",
    "token_type": "Bearer",
    "refresh_token": "8xLOxBtZp8",
    "expires_in": 3600,
    "id_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
}

4、请求用户信息或授权内的资源。
图片


隐式流程

在OAuth2.0中,隐式授权使用的response_type是token,而在OIDC中,变为了id_token token或是仅id_token,当仅使用id_token时,则不会返回Access token了。其余参数则和OAuth2.0中的隐式授权一致。但OIDC中相比OAuth2.0隐式流程额外增加了一个参数nonce

  • nonce:必填。字符串值,用于将第三方应用的会话与 Id token关联,并缓解重放攻击。该值未经修改地从身份验证请求传递到 Id token中。为了防止攻击者猜中值,随机数值中必须存在足够的熵。

注意:隐式流程中OAuth2.0的state和OIDC的nonce区别

https://stackoverflow.com/questions/46844285/difference-between-oauth-2-0-state-and-openid-nonce-parameter-why-state-cou

1、第三方应用(RP)携带参数跳转到授权服务器(OP)的认证授权页面,等待用户认证与执行授权。

图片

2、当授权服务器(OP)完成对用户认证信息的审核和授权范围的校验,重定向回第三方应用(RP)并按照请求时的response_type返回id token、access token(当仅设置id_token时不返回)。注意url的hash部分(#后面)。

图片

3、后续便是请求用户信息或是授权内的资源。


混合流程

这种是将授权码和隐式流程结合起来,一部分token来源于授权服务器(OP)重定向到第三方应用(RP)时带过来,一部分来源于第三方应用(RP)向授权服务器(OP)发起请求而得到。这种流程中,response_type,可以混合着用,如code id_token,code token或是code id_token token。

1、第三方应用(RP)向授权服务器(OP)发起认证请求。此处使用授权码和隐式中仅id_token的组合。

图片

2、当授权服务器(OP)完成对用户认证信息的审核和授权范围的校验,重定向回第三方应用(RP)并按照请求时的response_type返回id token、access token(当仅设置id_token时不返回)。

  • access_token:当response_type中有token选项时,则会返回access_token
  • id_token:当response_type中有id_token选项时,则会返回id_token
  • code:混合流程中,这个是必选的,所以总是会返回code。
    图片

3、第三方应用(RP)通过code来请求授权服务器(OP)换取Token。这和授权码流程一致了。


Id token格式

认证服务返回的ID Token需要严格遵守JWT(JSON Web Token)的定义,下面是JWT(JSON Web Token)的定义细节:

  • iss=Issuer Identifier: 必须。认证服务的唯一标识,一个区分大小写的https URL,不包含query和fragment组件。
  • sub=Subject Identifier:必须。iss提供的终端用户的标识,在iss范围内唯一,最长为255个ASCII个字符,区分大小写。
  • aud=Audience(s):必须。标识ID Token的受众,必须包含OAuth2的client_id,分大小写的字符串数组。
  • exp=Expiration time:必须。超过此时间的ID Token会作废。
  • iat=Issued At Time:必须。JWT的构建的时间。
  • auth_time=AuthenticationTime:终端用户完成认证的时间。
  • nonce:发送认证请求的时候提供的随机字符串,用来减缓重放攻击,也可以用来关联客户端Session。如果nonce存在,第三方应用必须验证nonce。
  • acr=Authentication Context Class Reference:可选。表示一个认证上下文引用值,可以用来标识认证上下文类。
  • amr=Authentication Methods References:可选。表示一组认证方法。
  • azpAuthorized party:可选。结合aud使用。只有在被认证的一方和受众(aud)不一致时才使用此值,一般情况下很少使用。
    如下是一个典型ID Token的示例:
{
    "exp": 1614187991,
    "iss": "https://authorization-server.com/oauth",
    "sub": "a0903223-a120-91e0-c342-10bd237c24c1",
    "aud": "client1",
    "iat": 1614151991,
    "auth_time": 0,
    "nonce": "n-0S6_WzA2Mj",
    "acr": "1",
    "azp": "client1",
    "nbf": 0,
    "typ": "ID",
    "session_state": "150df80e-92a1-4b0c-a5c5-8c858eb5a848",
    "userId": "123456",
    "preferred_username": "testera",
    "given_name": "tester",
    "family_name": "a",
    "email": "testera@myweb.com"
}

关于ID Token的更详细的定义请参见OpenID Connect Core 1.0


参考

https://openid.net/connect/

https://www.jianshu.com/p/2c602974bc98

https://baike.baidu.com/item/OpenID?wtp=tt

http://www.imooc.com/article/4167

https://www.cnblogs.com/linianhui/p/openid-connect-core.html

2022-03-31,望技术有成后能回来看见自己的脚步

posted @ 2022-03-31 22:48  微笑刺客D  阅读(533)  评论(0编辑  收藏  举报
返回顶部