从协议入手,剖析OAuth2.0(译 RFC 6749)
1、介绍
https://tools.ietf.org/html/rfc6749
传统的client-server授权模型,客户端通过使用凭证(通常的用户名和明文密码)访问服务端受保护的资源,为了能够让第三方应用程序访问受保护的资源,需要将凭证共享给第三方。
这就产生了一些问题和限制:
- 为了以后能够持续访问,第三方应用程序可能会存储凭证(用户名、密码)。
- 尽管密码本身存在安全弱点,但服务器必须支持密码验证。
- 由于无法限制受保护资源的访问粒度和期限,第三方应用程序获得了对受保护资源的广泛访问。
- 除了修改密码外,无法对单个第三方或者所有第三方吊销凭证。
OAuth通过引入一个授权层,并分离客户端和资源拥有者成不同的角色来解决这个问题。在OAuth中,通过发行不同的访问令牌(包括资源访问范围、生命周期、其他访问属性),而不是资源本身,来限制第三方应用程序访问受保护资源(资源拥有者保护并宿主在资源服务器)的粒度和期限,而不是直接把凭证(用户名和密码)提供给客户端。
例如:一个终端用户(资源拥有者)可以授权一个打印服务(客户端)来访问宿主在图片共享服务器(资源服务器)上的受保护的图片(资源),而不用共享凭证(用户名和密码)给打印服务(客户端)。
1.1 角色
在OAuth中定义了4中角色:
资源所有者(resource owner)
能够对受保护资源授予访问权的实体,当资源的所有者是人时,指的就是我们的终端用户。
资源服务(resource server)
宿主受保护的资源。能够接受和响应使用访问令牌(access tokens)对受保护资源的请求。
客户端(client)
对受保护资源发起请求的应用程序。可能是一个服务、桌面应用程序、或者其他设备。
授权服务(authorization server)
成功认证资源所有者和获得授权后,发行访问令牌给客户端。
授权服务和资源服务之间的交互不在超过该规范,授权服务和资源服务可能是同一个服务,也可能是不同的服务。单个授权服务器应该可以发行由多个资源服务器接受的访问令牌。
1.2 协议流程
+--------+ +---------------+ | |--(A)- Authorization Request ->| Resource | | | | Owner | | |<-(B)-- Authorization Grant ---| | | | +---------------+ | | | | +---------------+ | |--(C)-- Authorization Grant -->| Authorization | | Client | | Server | | |<-(D)----- Access Token -------| | | | +---------------+ | | | | +---------------+ | |--(E)----- Access Token ------>| Resource | | | | Server | | |<-(F)--- Protected Resource ---| | +--------+ +---------------+
(A) 客户端向资源所有者请求授权,这时授权请求可能直接提交给资源所有者,也可能提交给授权服务(扮演一个授权终端)。
(B) 客户端获得一个资源所有者授权许可的凭证。授权许可类型可以使用规范中定义的4中类型,也可以自定类型(注意:授权服务需要支持自定义授权许可类型)。
(C) 客户端通过提交授权许可凭证给授权服务,请求访问令牌。
(d) 授权服务认证客户端并验证授权许可凭证,颁发访问令牌。
(E) 客户端通过提交已认证的访问令牌,请求受保护的资源。
(F) 资源服务验证访问令牌,如果有效,响应请求。
注意:客户端获取获取授权许可凭证的首选方法,就是使用授权服务作为一个中介。
1.3 授权许可
代表资源所有者授权的一个凭据,可以用获取访问令牌;OAuth2.0 协议定义了4中授权许可类型:授权码模式、隐性模式、资源所有者密码凭证、客户端凭证。也可以自定义扩展的授权许可类型。
1.3.1 授权码模式(Authorization Code)
授权码通过一个授权服务器获得,授权服务在客户端和资源所有者之间扮演一个中介。客户端不是直接从资源所有者请求授权,而是将资源所有者指向授权服务器,然后授权服务器在将资源所有者引导到客户端,并携带授权码。在重定向资源服务器到客户端之前,授权服务器认证资源所有者并获得授权。注意授权服务器仅仅认证资源所有者,并不会共享凭证给客户端。
授权码提供了一些重要的安全好处,例如认证客户端,也可以用于直接获取令牌,而不再需要经过资源所有者,也可以暴露给其他人,包括资源所有者。
1.3.2 隐性模式(Implicit)
隐性模式是授权码模式的简化版,使用一个脚本语言(例如:JavaScript)优化了客户端的实现。在隐性模式中,资源所有者授权后,并不会为客户端颁发授权码,而是直接颁发一个访问令牌。因为并没有颁发中间凭证(例如:授权码),授权许可类型是隐性的,故称之为隐性模式。
在隐式授权流中发布访问令牌时,授权服务器不验证客户端。在某些情况下,客户端标识可以通过传递访问令牌给客户端的重定向URI来识别,访问令牌能够暴露给资源所有者和其他资源所有者访问的应用程序。隐性模式提高了某些客户端的响应速度和效率(例如:作为浏览器应用程序实现的客户端),因为它减少了获取访问令牌所需的往返次数。但是当安全性要求很高的场景下,这种模式需要权衡,特别是当授权码模式可用的情况下。
1.3.3 客户端的验证授权模式(Resource Owner Password Credentials)
客户端的验证授权(即用户名和密码)可以直接作为授权许可,来获得访问令牌。只有当资源所有者充分信任(例如:客户端是整个系统的一部分,或者是特权应用程序,或者其他授权许可不可用时)客户端的情况下,才使用该模式。尽管这种授权许可类型要求客户端直接访问资源所有者的凭据,但资源所有者凭证只能用于单个请求,来交换访问令牌。通过使用长寿命的访问令牌或者刷新令牌,该模式消除了客户端存储资源所有者凭证(以备将来使用)的需求。
1.3.4 客户端凭证模式(Client Credentials)
当授权范围仅限于受客户端控制的受保护资源时,客户端凭据可以用作授权许可。通常当客户以自己的名义行事时(此时,客户端也是一个资源所有者),客户端许可会被使用。
1.4 访问令牌(Access Token)
访问令牌是用于访问受保护资源的凭证。访问令牌是表示向客户机颁发的授权的字符串,该字符串通常对客户端不透明。该令牌带有特定的范围(控制资源访问粒度)和持续时间,由资源所有者授权许可,资源服务器和授权服务器强行执行。令牌可能表示用于检索授权信息的标识符,也可能以可验证方式自包含授权信息(一个访问令牌字符串由一些数据和一个签名信息组成)。
访问令牌是一个抽象的层,里面放置着各种各样资源服务器能够理解的构造信息(例如:资源的访问范围、持续时间等等)。通过抽象,也使访问令牌的颁发不获得授权许可更受限制,同时也消除了资源服务器需要理解多种认证方法的需要。
基于资源服务器安全(例如:加密属性)的需求,访问令牌可能有不同的格式、结构、使用方法。
1.5 刷新令牌(Refresh Token)
刷新令牌是一个用于获取访问令牌的凭证。刷新令牌由授权服务器颁发给客户端,如果当前的访问令牌无效或者过期时,获取一个新的访问令牌;或者强制再请求一个访问令牌(可能相同或更窄范围的访问令牌)。与资源所有者相比,访问令牌有更短的生命周期和更少的权限。对于授权服务器而言,颁发一个刷新令牌是可选的,但是当要颁发刷新令牌时,一般情况下,刷新令牌是伴随着访问令牌一起颁发的。
刷新令牌代表资源所有者对客户端给予授权许可的字符串,通过对客户端不透明。令牌表示用于检索授权信息的标识符。与访问令牌不同,刷新令牌仅用于授权服务器,且从不发送到资源服务器。
+--------+ +---------------+ | |--(A)------- Authorization Grant --------->| | | | | | | |<-(B)----------- Access Token -------------| | | | & Refresh Token | | | | | | | | +----------+ | | | |--(C)---- Access Token ---->| | | | | | | | | | | |<-(D)- Protected Resource --| Resource | | Authorization | | Client | | Server | | Server | | |--(E)---- Access Token ---->| | | | | | | | | | | |<-(F)- Invalid Token Error -| | | | | | +----------+ | | | | | | | |--(G)----------- Refresh Token ----------->| | | | | | | |<-(H)----------- Access Token -------------| | +--------+ & Optional Refresh Token +---------------+ 图 2: 刷新一个过期的访问令牌
(A) 客户端请求授权服务器的认证,并提交授权许可。
(B) 授权服务器认证客户端并验证授权许可后,颁发访问令牌和刷新令牌。
(C) 客户端向资源服务器发出受保护的资源请求,并提交访问令牌。
(D) 资源服务器验证访问令牌后,把受保护的资源响应给客户端。
(E) 步骤(C)和(D)重复,直到访问令牌过期。如果客户端知道访问令牌过期,就会跳到步骤(G)。否则,它将创建另一个受保护的资源请求。
(F) 由于访问令牌无效,资源服务器返回一个无效的令牌错误。
(G) 客户端请求一个新的访问令牌,并提交刷新令牌。客户端身份验证需求基于客户机类型和授权服务器策略。
(H) 授权服务器认证客户端并验证刷新令牌后,如果有效,颁发一个新的访问令牌(此时,是否颁发一个新的刷新令牌是可选的)。
1.6 TLS版本(TLS Version)
基于广泛部署和已知的安全漏洞,TLS版本都是随着时间变化而变化的。TLS版本1是最广泛部署的版本,将提供最广泛的互操作性。具体实现还可以支持满足其安全要求的附加传输层安全机制。
1.7 HTTP重定向(HTTP Redirections)
该规范充分利用了Http重定向,客户端和授权服务器将资源所有者用户代理重定向到另外的目的地。虽然规范中的示例显示了HTTP 302状态代码的使用,但是允许用户代理运用任何其他方法来完成此重定向,并被认为是实现细节。
1.8 互操作性(Interoperability)
OAuth 2提供了一个明确的安全特性丰富的授权框架。然而,作为一个丰富且高度可扩展的框架,有许多可选组件,这一规范本身可能会产生广泛的非互操作性实现。此外,该规范还提供部分必需的部分或完全未定义的组件(例如,客户端注册、授权服务器功能、端点发现)。没有这些组件,针对一个特定的授权服务器,资源服务器的互操作,客户端必须手动配置。这个框架的设计带有明确的期望,未来的工作将定义实现完整Web规模互操作性所必需的规范配置文件和扩展。
1.9 符号约定(Notational Conventions)
该协议中的关键词 "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT","SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL"在 [RFC2119] 中被详细的描述。该规范使用[RFC5234]中的 the Augmented Backus-Naur Form (ABNF)符号规范。此外,规则的URI引用被包含在“统一资源标识符(URI):通用语法”[ rfc3986 ]中。某些安全相关的术语被定义在[RFC4949]中,这些术语包括但不限于 "attack", "authentication", "authorization", "certificate","confidentiality", "credential", "encryption", "identity", "sign","signature", "trust", "validate", and "verify"。除非另有说明,所有协议参数名称和值都是区分大小写的。
2、客户端注册(Client Registration)
在启动协议之前,客户端使用授权服务器进行注册。客户端通过授权服务器注册的方式超过了该协议的范围,但是一般都是终端用户通过一个HTML表单提交注册。客户端注册并不需要客户端和授权服务器的直接交互,只要授权服务器支持,可以使用各种各样的放肆进行注册并获取已注册客户端的属性。例如:可以使用一个自发布或者第三方发布的声明(assertion)或者授权服务器通过一个信任的通道来进行客户端的发现。
当注册一个客户端时,客户端开发人员应该:
- 指定客户端类型
- 提供客户端重定向的URI
- 包括授权服务器所要求的任何其他信息(例如,应用程序名称,网站,描述,Logo图像,服务条款等等)。
2.1 客户端类型(Client Types)
基于客户端和授权服务器,安全认证的能力(例如:客户凭证的保密性),OAuth定义了两种类型的客户端。
保密类型(confidential)
能够维护其凭证的保密性的客户端(例如:客户端在一个安全的服务器上,客户端的访问受到限制),能够通过其他方式来保证客户端认证的安全性。
公开类型(pulic)
不能够维护其凭证的保密性的客户端(例如:在资源所有者的设备上执行客户端,如已安装的本机应用程序或基于Web浏览器的应用程序),不能够通过其他方式来保证客户端认证的安全性。
客户机类型指定是基于授权服务器对安全认证的定义及其可接受的客户端证书的暴露级别。授权服务器不应该对客户端类型做出假设。客户机可以实现为一组分布式组件,每个组件具有不同的客户机类型和安全上下文(例如,一个分布式客户端既有基于服务器的机密组件,又有基于公共浏览器的组件);如果传统授权服务器不能够对这样的客户端提供支持或者注册引导,客户应将每个组件注册为单独的客户机。
本规范是围绕以下客户端配置文件设计的:
web 应用程序(web application)
Web应用程序是在Web服务器上运行的机密客户端。资源所有者通过在其使用的设备上的通过用户代理(浏览器)中呈现的HTML用户界面访问客户端。客户端凭据以及向客户机发出的任何访问令牌都存储在Web服务器上,不会被资源所有者暴露或访问。
基于用户代理的应用程序(user-agent-based application)
基于用户代理的应用程序是一个公共客户端,其中客户端代码从Web服务器下载并在由资源所有者所使用的设备的用户代理内执行。 所以,对于资源所有者,协议数据和凭证可以很容易获得。由于这些应用程序驻留在用户代理中,所以在请求授权时可以无缝地使用用户代理功能。
本地应用程序(native application)
本机应用程序是安装在资源所有者使用的设备上并执行的公共客户机。对于资源所有者,协议数据和证书是可得到的。假设可以提取应用程序中包含的任何客户端身份验证凭据。另一方面,动态发布的凭据(如访问令牌或刷新令牌)可以收到可接受的保护级别。至少,这些凭据被保护免受应用程序可能交互的恶意服务器的保护。某些情况下,这些应用程序也会受到保护免受驻留在同一设备的其他应用程序的干扰。
2.2 客户端标识(Client Identifier)
授权服务器向注册客户端颁发客户端标识符------表示客户端提供的注册信息的唯一字符串。客户机标识符不是秘密,它暴露给资源所有者,不能单独用于客户端身份验证。
客户端标识符对授权服务器是唯一的。
客户标识符字符串大小该规范未定义。客户机应该避免对标识符大小做出假设。授权服务器应该记录它所颁发的任何标识符的大小。
2.3 客户端认证(Client Authentication)
如果客户机类型是机密的,客户机和授权服务器建立适合于授权服务器的安全需求的客户端身份验证方法。授权服务器可以接受满足其安全要求的任何形式的客户端身份验证。机密客户机通常发布(或建立)一组客户端证书,用于与授权服务器进行身份验证(例如,密码、公钥/私钥对)。授权服务器可以和公共客户端建立客户端认证方法。但是,授权服务器不能依赖公共客户端身份验证,以识别客户机。在每个请求中,客户端不能使用多个身份验证方法。
2.3.1 客户端密码(Client Password)
拥有客户端密码的客户机可以使用HTTP基本身份验证方案和授权服务器进行身份验证。客户端标识使用"application/x-www-form-urlencoded"编码算法进行编码,编码值用作用户名。客户端密码使用相同的算法进行编码,并用作密码。授权服务器必须支持HTTP基本身份验证方案,以验证客户端密码并颁发密码。
例如:授权 Basic czZCaGRSa3F0Mzo3RmpmcDBaQnIxS3REUmJuZlZkbUl3,还有一种可选的方案就是,在请求头中包含两个参数(client_id和client_secret),这种方案一般不推荐,除非客户端不能够使用 HTTP Basic authentication scheme (或者其他的基于密码的HTTP authentication scheme)。参数只能在请求体中传输,不能包含在请求URI中。
2.3.1 其他认证方法(Other Authentication Methods)
授权服务器可以支持符合其安全要求的任何合适的HTTP认证方案。在使用其他身份验证方法时,授权服务器必须定义客户端标识符(注册记录)和身份验证方案之间的映射。
2.4 未注册的客户端(Unregistered Clients)
该规范没有包含未注册的客户端的使用。这种客户机的使用超出了本规范的范围,需要对其互操作性影响进行额外的安全性分析和审查。
3、协议的端点(Protocol Endpoints)
授权处理利用两个授权服务器终结点:
- 授权端点
客户端通过用户代理重定向到该端点获得资源所有者的授权。
- 令牌端点
客户机用来交换访问令牌的授权许可,通常具有客户端身份验证。
- 重定向端点
授权服务器用于通过资源所有者用户代理将包含授权凭证的响应返回给客户端。
并不是每一个授权许可类型都会使用这些终结点;当然扩展的授权许可类型可以定义自己的终结点。
3.1 授权端点(Authorization Endpoint)
授权终结点与资源所有者交互获取一个授权许可。首先授权服务器必须审核资源所有者的身份信息(授权服务器采用什么方式认证资源所有者的身份(例如:用户名、密码、会话Cookie),超出了本规范的访问)。
授权URI可能包含 "application/x-www-form-urlencoded" 格式的必须的查询组件(query parameters)。端点URI不能包含碎片组件(fragment component)。
由于对授权端点的请求,导致用户身份验证和明文证书的传输。当发送请求到授权端点时,授权服务器必须要求使用TLS。授权服务器必须支持“Get”方法,也可以支持“Post”方法。
发送请求时,没有值的参数会被省略。授权服务器必须忽略无法识别的请求参数。请求和响应参数不应包含超过一次。
3.1.1 响应类型(Response Type)
授权终结点被授权码模式和隐式模式使用。
客户端通过下列参数来通知授权服务器期望建立的授权许可类型流:
response_type:必须。授权码模式时,该值必须为“code”。隐性模式时,该值必须为“token”。还可以为我们自己注册的扩展值。如果一个授权请求确实该参数,或者授权服务器无法理解该参数,将返回一个错误的响应。
3.1.2 重定向终结点(Redirection Endpoint)
在完成与资源所有者的交互之后,授权服务器将资源所有者的用户代理引导回客户端。重定向终结点URI必须为完整的URI,端点的URI可能包括一个“application/x-www-form-urlencoded”格式的查询组件,但不能包含“fragment ”组件。
3.1.2.1 终结点请求机密性(Endpoint Request Confidentiality)
当请求的响应类型为“code”或“token”时,或者当我们要传送机密的凭证信息时,这个时候需要使用TLS。该规范并不强制使用TLS,因为在编写此规范时,要求客户机部署TLS是许多客户机开发人员的一个重要障碍。如果TLS不可用,在重定向到授权服务器之前,应该警告资源所有者不安全端点的情况。
传输层安全性的缺乏会严重影响客户端和授权访问的受保护资源的安全性。当授权过程作为客户端委托的终端用户身份验证的一种形式时(例如:第三方登录服务),传输层安全性的使用尤为重要。
3.1.2.1 注册要求(Registration Requirements)
授权服务器只接受下面的客户端的注册:
- 公共客户端
- 使用隐式授权类型的机密客户端
在使用授权服务器之前,应该要求所有的客户端来注册他们的完成授权后的重定向端点。客户机可以使用“state”请求参数来实现每个请求定制。如果要求完全重定向URI的注册是不可能的,授权服务器应该要求URI scheme, authority, and path的注册(允许客户端动态的改变重定向URI的查询组)。授权服务器也允许客户端注册多个重定向端点。缺少重定向URI注册要求,增加攻击者利用授权端点的风险。
3.1.2.1 动态配置(Dynamic Configuration)
如果多个重定向URI已注册,或者只有重定向URI的一部分已被注册,或者重定向URL没有被注册,则在客户端的授权请求的查询组件中,必须包含"redirect_uri"参数。当一个重定向的URI被包含在授权请求中时,授权服务器必须跟以注册的客户端重定向URI作比较和匹配,如果没有就跟客户端本身的URL作比较。
3.1.2.2 无效端点(Invalid Endpoint)
如果由于授权请求参数缺失,无效,或者不能匹配无效的URI,授权服务器应该告知一个错误给资源所有者。
3.1.2.2 端点内容(Endpoint Content)
返回给客户端的请求通常是一个HTML文档。如果HTML响应作为重定向请求的结果直接送达,则HTML文档中包含的任何脚本都将执行对重定向URI及其所包含的凭据的完全访问。在重定向端点的响应中,客户端不应该包含任何第三方的脚本,相反它应该提取出凭证,然后重定向用户代理到另外的不会暴露凭证信息的端点。如果,包含第三方脚本,客户端必须保证首先执行自己的脚本。
3.2 令牌端点(Token Endpoint)
令牌端点被客户端用来获取访问令牌(通过呈现一个包含授权信息的授权许可)或者刷新令牌。除了隐式授权类型(访问令牌是直接颁发的。)外,其他的三种授权许可类型都会使用令牌端点。
客户端获取令牌端点地址的方法超过了本规范的范畴,但是通常都是在一个服务文档中提供。
因为对令牌端点的请求会涉及到凭证的明文传输,所有要求必须使用TLS,并且必须使用Post方法。
3.2.1 客户端授权(Client Authentication)
当向令牌端点发起请求时,为机密客户端和其他的客户端颁发的客户端凭证必须和授权服务器进行授权。
- 强制颁发刷新令牌和授权码给客户端。当授权码被以一种非安全的方式传输到重定向端点,或者重定向URI没有被完全的注册。
- 通过禁用客户端或更改其凭据来,从受损客户机中恢复,从而防止攻击者滥用被盗的刷新令牌。毕竟改变单一的一组客户端凭证比注销整个刷新令牌要快的多。
- 实现认证管理最佳实践,需要定期证书轮换。实现整套刷新令牌的轮换是具有挑战性的,而实现单一的客户端证书轮换非常的简单。
当发送请求的令牌端点时,客户可以使用“client_id”请求参数标识本身。在通过“authorization_code”和“grant_type”对令牌端点发起请求时,未经身份验证的客户端必须发送“client_id”以防止自己无意间接受一个来自于其他客户端“client_id”的代码。这样也防止客户端的授权码被替换,但是这并不对受保护的资源提供额外的安全性保障。
3.2 访问令牌范围(Access Token Scope)
通过在请求中添加参数"scope",授权端点和令牌端点允许客户端指定请求所访问的范围。同样的,授权服务器也是用“scope”响应参数来表示颁发给客户端的访问令牌的范围。“scope”参数的值表示为空格分隔的、区分大小写的字符串的列表。该字符串由授权服务器定义。
基于授权服务器的策略和资源所有者的指示,授权服务器可能会完全或者部分忽略客户端请求的“scope”参数。如果已发布的访问令牌范围与客户端请求的范围不同,授权服务器必须包含“scope”响应参数,告知客户端实际范围授予。
如果客户端在请求授权时省略了范围参数,则授权服务器必须使用预先定义的范围默认值处理请求,或者失败指示无效范围的请求。授权服务器应该记录它的范围要求和默认值。
4、获取授权(Obtaining Authorization)
为了获得一个访问令牌,客户端需要向资源所有者获得授权,授权以许可(authorization grant)的形式被呈现。OAuth定义了4中许可类型:authorization code(授权码), implicit(隐性),resource owner password credentials(资源所有者密码凭证), and client credentials(客户端凭证)。它也提供了扩展的机制来定义额外的授权许可类型。
4.1 授权码许可类型(Authorization Code Grant)
授权码许可类型可以用于获取访问令牌和刷新令牌,它是为机密客户端凭证优化的。由于这是一个基于重定向的流程,客户端必须能够与资源所有者的用户代理(通常是一个webbrowser)进行交互,并能够接收来自于授权服务器的传入请求(重定向)。
+----------+ | Resource | | Owner | | | +----------+ ^ | (B) +----|-----+ Client Identifier +---------------+ | -+----(A)-- & Redirection URI ---->| | | User- | | Authorization | | Agent -+----(B)-- User authenticates --->| Server | | | | | | -+----(C)-- Authorization Code ---<| | +-|----|---+ +---------------+ | | ^ v (A) (C) | | | | | | ^ v | | +---------+ | | | |>---(D)-- Authorization Code ---------' | | Client | & Redirection URI | | | | | |<---(E)----- Access Token -------------------' +---------+ (w/ Optional Refresh Token)
注意:通过上图可以看到,到客户端经过用户代理时,(A)、(B)、(C)被分成了两步。
图3:授权码流程
(A)客户端通过重定向资源所有者的用户代理重定向到授权服务器(注意资源服务与授权服务不是同一台服务器),并在请求中包含客户端标识符、请求的范围、本地状态、重定向URI等参数,一旦授权服务器许可或者拒绝后,就会返回到用户代理。
(B)授权服务器认证资源所有者(通过用户代理),确定资源所有者的许可,或者拒绝客户端的访问请求。
(C)假如资源所有者许可访问,根据早前在客户端认证阶段提供的重定向URI,并携带参数授权码和本地状态(早前在客户端认证阶段提供的),重定向用户代理到客户端。
(D)通过在请求中包含授权码和重定向URI,客户端从令牌端点获取访问令牌。
(E)授权服务器验证授权码和重定向URI(和客户端认证阶段提供的重定向URI进行匹配),如果有效,携带访问令牌和刷新令牌(可选)响应返回。
4.1.1 授权请求(Authorization Request)
通过添加下列参数到查询组件(query component)中(注意使用 "application/x-www-form-urlencoded" ),发起对授权端点URI的请求。
response_type(请求类型):必须,值必须为“code”
client_id(客户端身份标识):必须
redirect_uri(重定向URI):可选
scope(请求的范围):可选
state(客户端本地状态):必须,客户机用来维护请求和回调之间的状态的不透明值。当将用户代理重定向回客户端时,授权服务器包含此值。该参数应用于防止跨站点请求伪造。
通过Http重定向响应或者其他用户代理能够接受的方式,客户端将资源所有者导向已构建好的URI。例如,客户端指导用户代理来发起下面的Http请求:
GET /authorize?response_type=code&client_id=s6BhdRkqt3&state=xyz&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb HTTP/1.1
Host: server.example.com
授权服务器验证请求来保证所有必须的参数都存在和有效。如果请求有效,授权服务器通过认证资源所有者来获得授权决定。如果授权决定被许可,授权服务器就会重定向用户代理到客户端提供的重定向URI。
4.1.2 授权响应(Authorization Response)
如果资源所有者许可访问请求,授权服务颁发一个授权码,并以"application/x-www-form-urlencoded"格式添加下列参数到重定向URI查询组件(query)中。
code:必须。由授权服务器生成的授权码。授权码被颁发后,必须短暂过期,降低泄露风险,最长的生命周期推荐10分钟,客户端只能使用一次授权码;如果被第二次使用时,授权服务器必须注销颁发给该授权码的所有令牌。
state:状态。如果该参数在之前的客户端授权请求中被包含,则返回时必须原封不动的带上此参数。
例如:授权服务器通过发送下面的Http 响应重定向用户代理到客户端。
HTTP/1.1 302 Found
Location: https://client.example.com/cb?code=SplxlOBeZQQYbYS6WxSbIA&state=xyz
客户端必须忽略不能识别的响应参数,授权码字符串的长度本规范并未定义。客户端应该避免对授权码长度的猜测,授权服务器应该记录下所颁发的授权码的长度。
4.1.2.1 错误响应(Error Response)
如果由于缺失、无效、不匹配重定向URI和客户端标识符,授权服务器应该通知资源所有者错误,不能自动的重定向用户代理到无效的重定向URI。
如果资源所有者拒绝客户端的访问请求,授权码服务器应该使用"application/x-www-form-urlencoded"格式添加下列参数到重定向URI的查询组件(query component)中。
error:必须。一个简单的、来自于包含下面参数的 ASCII 错误代码。
invalid_request(无效请求):请求缺少所需的参数,包括无效的参数值,其中包含一次以上的参数,或者是其他格式错误的。
unauthorized_client(未授权的客户端):客户端没有授权使用此方法请求授权码。
access_denied(访问拒绝):资源所有者或者授权服务器拒绝请求。
unsupported_response_type(不支持的响应类型):授权服务器不支持使用此方法获得授权代码。
invalid_scope(无效的请求范围):请求的范围无效、未知或格式错误。
server_error(服务器错误):授权服务器遇到意外情况,阻止它执行请求(因为当重定向时,一个500内部错误Http状态码不能返回给客户端,所以这个错误码是必须的。)。
temporarily_unavailable(暂时不可用):由于临时服务器超载或维护,授权服务器目前无法处理请求。
error_description(错误的描述):可选。提供额外信息,人类可读的ASCII码文本字符串。帮助客户端的开发者理解发生的错误。
error_uri(错误URI):可选。带有错误信息的且可读的Web页面,用于向客户端开发人员提供关于错误的附加信息。
state(状态):可选。如果在客户机授权请求中存在“state”参数,则需要原封不动的返回。
例如:授权服务器通过发送下面的Http 响应重定向用户代理到客户端。
HTTP/1.1 302 Found
Location: https://client.example.com/cb?error=access_denied&state=xyz
4.1.3 访问令牌请求(Access Token Request)
客户端对令牌端点发送请求,并添加下列参数到查询组件(query component)中(注意使用 "application/x-www-form-urlencoded" ),在在HTTP请求实体中使用utf-8字符编码。
grant_type(授权许可类型):必须。值必须为“authorization_code”
code(授权码):必须。从认证服务器请求的授权码。
redirect_uri(重定向URI):必须。如果重定向URI被包含在授权请求中,则值必须与客户端认证阶段包含的重定向URI相同。
client_id(客户端身份标识):必须。如果客户端不使用授权服务器进行身份验证。如果客户端类型是机密的,或者客户端已经被颁发的凭证,或者被分配其他的授权要求,则客户端必须使用授权服务器进行身份验证。
例如,客户端使用TLS发送以下HTTP请求:
POST /token HTTP/1.1
Host: server.example.com
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code&code=SplxlOBeZQQYbYS6WxSbIA
&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb
授权服务器必须:
-
- 如果客户端类型是机密的,或者客户端已经被颁发的凭证,或者被分配其他的授权要求,则客户端必须使进行身份验证。
- 如果客户端认证被包含,则认证客户端。
- 确保授权码被发送到经身份验证的机密客户机,或者被颁发给请求中“client_id”标识的公开客户端。
- 审核授权码是否有效。
- 如果“redirect_uri”被包含在请求中,确保与之前客户端认证阶段提供的“redirect_uri”参数相等。
- 如果客户端类型是机密的,或者客户端已经被颁发的凭证,或者被分配其他的授权要求,则客户端必须使进行身份验证。
4.1.4 访问令牌响应(Access Token Response)
如果访问令牌请求有效并已被授权,则授权服务需颁发一个必须的访问令牌和一个可选的刷新令牌。如果请求客户端认证失败或者无效,授权服务器将返回错误的响应。
成功响应的例子:
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache
{
"access_token":"2YotnFZFEjr1zCsicMWpAA",
"token_type":"example",
"expires_in":3600,
"refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",
"example_parameter":"example_value"
}
4.2 隐性许可类型(Implicit Grant)
隐式许可类型用于获取访问令牌(不支持发行刷新令牌),并且它对运行特定重定向URI的公共客户机进行了优化。 这些客户机通常使用JavaScript等脚本语言实现,并运行在客户端中。由于这是一个基于重定向的流程,客户端必须能够与资源所有者的用户代理(通常一个web浏览器)进行交互,并且能够接受来自于授权服务传入(通过重定向)的请求。
不像授权码许可类型,客户端分别请求授权和访问令牌,而是直接接收访问令牌作为授权请求的结果。 隐式授权类型不包括客户端身份验证,依赖于资源所有者的存在和重定向URI的注册。因为访问令牌被编码到重定向URI中,所以它可能暴露在资源所有者和驻留在同一设备上的其他应用程序中。
+----------+ | Resource | | Owner | | | +----------+ ^ | (B) +----|-----+ Client Identifier +---------------+ | -+----(A)-- & Redirection URI --->| | | User- | | Authorization | | Agent -|----(B)-- User authenticates -->| Server | | | | | | |<---(C)--- Redirection URI ----<| | | | with Access Token +---------------+ | | in Fragment | | +---------------+ | |----(D)--- Redirection URI ---->| Web-Hosted | | | without Fragment | Client | | | | Resource | | (F) |<---(E)------- Script ---------<| | | | +---------------+ +-|--------+ | | (A) (G) Access Token | | ^ v +---------+ | | | Client | | | +---------+
图4:隐性许可流程
(A)客户端将资源服务器的用户代理导向授权端点,并包含客户端标识、请求范围、本地状态、重定向URI等参数,一旦访问被许可,授权服务器将返回到用户代理。
(B)授权服务器认证资源所有者,并确定资源所有者是许可还是拒绝客户端的访问请求。
(C)假如资源所有者许可访问,根据早前在客户端认证阶段提供的重定向URI,并在URI片段(fragment)中包含访问令牌,重定向用户代理到客户端。
(D)用户代理保留本地UIR片段,向客户端Web托管资源服务器发起请求。
(E)客户端Web托管资源服务器返回一个web页面,并包含能够在URI片段中的提取访问令牌(和其他参数)的脚本。
(F)用户代理执行脚本,提取访问令牌(和其他参数)
(G)用户代理将访问令牌传送给客户端。
4.2.1 授权请求(Authorization Request)
通过添加下列参数到查询组件(query component)中(注意使用 "application/x-www-form-urlencoded" ),发起对授权端点URI的请求。
response_type(响应类型):必须。值必须为“Token”。
client_id(客户端标识):必须。
redirect_uri(重定向URI):可选。
scope(请求的范围):可选。
state(客户端本地状态):推荐。客户机用来维护请求和回调之间的状态的不透明值。当将用户代理重定向回客户端时,授权服务器包含此值。该参数应用于防止跨站点请求伪造。
通过Http重定向,客户端将资源所有者导向使用上面参数构造的授权服务器URI请求。例如,客户端指导用户代理使用TLS来发起下面的Http请求:
GET /authorize?response_type=token&client_id=s6BhdRkqt3&state=xyz
&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb HTTP/1.1
Host: server.example.com
授权服务器验证请求来保证所有必须的参数都存在和有效。授权服务器必须(因为需要它来携带访问令牌)验证重定向URI,并与之前客户端认证提供的重定向URI进行匹配。如果请求有效,授权服务器通过认证资源所有者来获得授权决定(或者通过其他标准来获得授权许可)。如果授权决定被许可,授权服务器就会重定向用户代理到客户端提供的重定向URI。
4.2.2 授权响应(Authorization Response)
如果资源所有者许可访问请求,授权服务器就会颁发一个访问令牌,并以"application/x-www-form-urlencoded"格式添加下列参数到重定向URI片段组件(query fragment)中。
access_token(访问令牌):必须。
token_type(令牌类型):必须。
expires_in(过期时间):推荐。访问令牌的生命周期,值“3600”表示访问令牌将在一个小时后过期。如果省略,授权服务器,应该设置一个默认值,或者通过其他的方式提供过期时间。
scope(范围):可选。如果等同于客户端请求的范围则可选,否则必须。
state(状态):可选。如果在客户机授权请求中存在“state”参数,则必须原封不动的返回。
授权服务器不能颁发刷新令牌
例如:授权服务器通过发送下面的Http 响应重定向用户代理。
HTTP/1.1 302 Found Location: http://example.com/cb#access_token=2YotnFZFEjr1zCsicMWpAA &state=xyz&token_type=example&expires_in=3600
开发人员应该注意到一些用户代理不支持在HTTP“location”响应头字段中包含一个片段组件。这样的客户端会要求使用其他方法重定向客户端,而不是一个3xx重定向响应。例如,返回一个HTML页面,其中包含一个与重定向URI链接的动作的“继续”按钮。
客户端必须忽略不能识别的响应参数,授权码字符串的长度本规范并未定义。客户端应该避免对授权码长度的猜测,授权服务器应该记录下所颁发的授权码的长度。
4.2.2.1 错误响应(Error Response)
如果由于缺失、无效、不匹配重定向URI和客户端标识符,授权服务器应该通知资源所有者错误,不能自动的重定向用户代理到无效的重定向URI。
如果资源所有者拒绝客户端的访问请求,授权码服务器应该使用"application/x-www-form-urlencoded"格式添加下列参数到重定向URI的查询组件(query component)中。
error:必须。一个简单的、来自于包含下面参数的 ASCII 错误代码。
invalid_request(无效请求):请求缺少所需的参数,包括无效的参数值,其中包含一次以上的参数,或者是其他格式错误的。
unauthorized_client(未授权的客户端):客户端没有授权使用此方法请求授权码。
access_denied(访问拒绝):资源所有者或者授权服务器拒绝请求。
unsupported_response_type(不支持的响应类型):授权服务器不支持使用此方法获得授权代码。
invalid_scope(无效的请求范围):请求的范围无效、未知或格式错误。
server_error(服务器错误):授权服务器遇到意外情况,阻止它执行请求(因为当重定向时,一个500内部错误Http状态码不能返回给客户端,所以这个错误码是必须的。)。
temporarily_unavailable(暂时不可用):由于临时服务器超载或维护,授权服务器目前无法处理请求。
error_description(错误的描述):可选。提供额外信息,人类可读的ASCII码文本字符串。帮助客户端的开发者理解发生的错误。
error_uri(错误URI):可选。带有错误信息的且可读的Web页面,用于向客户端开发人员提供关于错误的附加信息。
state(状态):可选。如果在客户机授权请求中存在“state”参数,则需要原封不动的返回。
例如:授权服务器通过发送下面的Http 响应重定向用户代理到客户端。
HTTP/1.1 302 Found
Location: https://client.example.com/cb#error=access_denied&state=xyz
4.3 资源所有者密码凭证许可类型(Resource Owner Password Credentials Grant)
只有当资源所有者充分信任(例如:客户端是整个系统的一部分,或者是特权应用程序,或者其他授权许可不可用时)客户端的情况下,才使用该模式。当使用该模式时,授权服务器应该特别小心,并且只有在其他授权流不可用的情况下,才使用该模式。
此授权类型适用于能够获得资源所有者证书的客户端(用户名和密码,通常使用交互式表单)。它还用于迁移现有客户使用直接的认证方案,如HTTP基本或摘要通过将存储的凭据来访问令牌的OAuth认证。
+----------+ | Resource | | Owner | | | +----------+ v | Resource Owner (A) Password Credentials | v +---------+ +---------------+ | |>--(B)---- Resource Owner ------->| | | | Password Credentials | Authorization | | Client | | Server | | |<--(C)---- Access Token ---------<| | | | (w/ Optional Refresh Token) | | +---------+ +---------------+
图5:资源所有者密码凭证流程
(A)资源所有者向客户端提供用户名和密码。
(B)客户端通过包含从资源所有者收到的凭据,请求来自授权服务器令牌端点的访问令牌。当发起请求时,客户端与授权服务器进行认证。
(C)授权服务器认证客户端,并验证资源所有者凭证,如果有效,颁发一个访问令牌。
4.3.1 授权请求和响应(Authorization Request And Response)
客户机获取资源所有者证书的方法超出了本规范的范围。一旦访问令牌获得,客户端必须丢弃凭据(理想状态)。
4.3.2 访问令牌请求(Access Token Request)
客户端对令牌端点发送请求,并添加下列参数到查询组件(query component)中(注意使用 "application/x-www-form-urlencoded" ),在在HTTP请求实体中使用utf-8字符编码。
grant_type(授权许可类型):必须。值必须为“password” 。
username(用户名):必须 。
password(密码):必须。
scope(请求的范围):可选
例如:客户端使用TLS发送下面的请求=>
POST /token HTTP/1.1 Host: server.example.com Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW Content-Type: application/x-www-form-urlencoded
grant_type=password&username=johndoe&password=A3ddj3w
授权服务器必须:
- 如果客户端是机密的,则授权服务器必须认证客户端。
- 如果客户端凭证存在,则认证客户端。
- 使用存在的密码验证策略,验证资源所有者密码凭证。
由于此访问令牌请求使用资源所有者的密码,授权服务器必须保护端点不受暴力攻击(例如使用速度限制、验证码、弹窗等等)。
4.3.4 访问令牌响应(Access Token Response)
如果访问令牌请求有效并已被授权,则授权服务需颁发一个必须的访问令牌和一个可选的刷新令牌。如果请求客户端认证失败或者无效,授权服务器将返回错误的响应。
成功响应的例子:
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache
{
"access_token":"2YotnFZFEjr1zCsicMWpAA",
"token_type":"example",
"expires_in":3600,
"refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",
"example_parameter":"example_value"
}
4.4 客户端许可类型(Client Credentials Grant)
当客户端仅仅想使用自己的凭证请求访问受其控制的,或请求授权服务器已经配置的另一个资源所有者的受保护资源。
客户凭证许可类型必须仅由机密客端使用。
+---------+ +---------------+ | | | | | |>--(A)- Client Authentication --->| Authorization | | Client | | Server | | |<--(B)---- Access Token ---------<| | | | | | +---------+ +---------------+ 图 6: 客户端凭证流程
(A)客户端和授权服务器进行认证,并向令牌端点请求访问令牌。
(B)授权服务器认证客户端,如果,有效,则颁发访问令牌。
4.4.1 授权请求和响应(Authorization Request And Response)
因为客户端凭证被作为授权许可,所有不需要其他额外的授权请求。
4.4.2 访问令牌请求(Access Token Request)
客户端对令牌端点发送请求,并添加下列参数(注意使用 "application/x-www-form-urlencoded" 格式和utf-8字符编码)在HTTP请求实体中。。
grant_type(授权许可类型):必须。值必须为“client_credentials” 。
scope(请求的范围):可选
例如:客户端使用TLS发送下面的请求=>
POST /token HTTP/1.1
Host: server.example.com
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials
授权服务器必须认证客户端。
4.4.4 访问令牌响应(Access Token Response)
如果访问令牌请求有效并已被授权,则授权服务需颁发一个必须的访问令牌和一个可选的刷新令牌。如果请求客户端认证失败或者无效,授权服务器将返回错误的响应。
成功响应的例子:
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache
{
"access_token":"2YotnFZFEjr1zCsicMWpAA",
"token_type":"example",
"expires_in":3600,
"example_parameter":"example_value"
}
4.5 扩展许可类型(Extension Grant)
通过与授权服务器进行沟通,客户端将授权服务器定义好的授权许可类型的值作为参数“grant_type”的值,也可以包含其他的一些参数,向令牌端点发送请求。