1. 简介

在传统的CS认证模型中,客户端通过使用资源拥有者(也就是用户)的凭证去请求服务器上的受限制资源。

客户端不是浏览器,浏览器是用户代理,在HTTP请求头中User-Agent指出的浏览器的版本、内核,而User-Agent是用户代理的意思,即浏览器帮助用户与服务器通信。真正的客户端是可以代表用户操作的任何应用,如应用的前端网页,Windows的桌面应用等等。

用户输入密码(即给予浏览器用户的凭证),让client替用户通过浏览器去访问用户在服务器上的受保护资源并呈现。

就如Basic认证,把“用户名+冒号+密码”用BASE64加密后,放到Authrization请求头中发送给服务器,每次请求都得加上Authorization请求头,需要配合HTTPS使用。

为了让第三方应用访问我们在服务器上受保护的资源,Resource Owner(用户)不得不将自己的凭证交给第三方应用,这就会产生了一些问题和限制:

  1. 第三方应用必须存储Resource Owner的凭证用以在将来访问时使用,即存储客户的明文密码。(非常不安全
  2. 服务器必去支持密码认证,尽管密码固有的安全弱点。(没理解,但可能是想表达用户已经将密码交出,密码认证不再有安全性可言)
  3. Resource Owner没有任何手段去限制第三方应用的访问时间,或者限制其访问范围;这导致第三方应用获得过大的访问权限。
  4. Resource Owner想要撤销某个第三方应用的访问权限时,会撤销所有第三方应用的访问权限,并且只能通过修改密码的方式撤销。
  5. 任何一个第三方应用泄露凭证(密码)都会造成用户的密码和所有受这个密码保护的资源被泄露。

OAuth通过引入一个授权层解决这些问题,并将客户端的角色与Resource Owner角色分离(传统的CS认证中,与服务器直接通信的是Client,也就是浏览器,用户与客户端角色模棱两可)。

在OAuth中,客户端被颁发一组与用户凭证完全不同的凭证(access_token),这个凭证是一个字符串,它包括访问范围,过期时间和其它与访问相关的属性。

如果用户同意授权给第三方应用,认证服务器会将access_token颁发给第三方应用客户端。第三方客户端籍此访问资源服务器托管的受保护资源。

1.1 角色

OAuth定义了四种角色:

  1. Resource Owner:能够授权访问受保护资源的实体。如果Resource Owner是一个人,那么它也叫最终用户
  2. Resource Server:托管受保护资源的服务器,能够使用access token处理对受保护资源的请求
  3. Client:代表Resource Owner访问受保护资源且有其授权的任何应用。
  4. Authorization Server:成功认证Resource Owner,并且Client获得用户授权后,Authorization Server向Client颁发access token。

Resource Server与Authorization Server可以是同一个服务,他们合称为服务提供商,Authorization Server颁发的access token能够被多个Resource Server接受。

1.2 协议流程

(A) Client向Resource Owner请求授权。授权请求可以由Client直接向Resource Owner发出,或者最好通过授权服务器间接发出(即通过重定向到Authorization Server授权页)。推荐后者

(B) Client收到Resource Owner的授权许可,授权许可的类型是此规范中定义的四种授权类型之一或者是一个扩展授权类型。授权类型取决于Client请求授权的方法和Authorization Server支持的授权类型。

© Client通过向授权服务器进行认证,且出示授权许可来请求access token。

(D) Authorization Server鉴定客户端并且验证授权许可,如果有效,则颁发access token

(E) Client通过access_token请求Resource Server上的受保护资源

(F) Resource Server鉴定access token,如果有效,处理此次请求

1.3 授权许可类型

授权许可是一个象征资源拥有者授权的凭证,它被Client用来获取access token。OAuth2.0规范定义四个授权类型,也支持自定义授权类型。

1.3.1 Authorization code

授权码是从授权服务器获取的,授权服务器作为Client和资源拥有者之间的中介。

Client引导资源拥有者访问授权服务器来发起授权,授权服务器引导用户携带授权码返回Client之前,会认证资源拥有并生成授权码。因此用户的凭证并不会与Client分享。之后使用授权码请求access token。

上图GitHub按钮就是Client引导资源拥有者的方式,鼠标放上去时,浏览器坐下角的地址为

此请求返回302响应

重定向地址为

结果为

当点击同意授权时,会请求

请求体为

1.3.2 Implicit

隐含模式是授权码模式的简化。隐含模式在用户授权后直接给Client颁发access token。

隐含模式颁发access token时不会鉴定Client,在某些情况下,可以使用redirect url来验证Client

1.3.3 Resource owner password credentials

资源拥有者密码凭证可以直接用做授权许可来获取access token。此模式,在资源拥有着与Client之间高度信任(例如用支付宝账号密码登录淘宝),或其它授权类型不可用时可用。

1.3.4 Client credentials

客户端凭证模式适用于Client要访问的受保护资源本就在Client的控制下时,即客户端为自己行事(客户端也是资源拥有者)

1.4 Access Token

Access token是象征授权的一个字符串,用于访问受保护资源,它对于Client是不透明的。

1.5 Refresh Token

Refresh Token是Access Token过期、无效时获取新的access token是的凭证。它也有Authorization Server颁发,且与Access Token同时颁发。它对于Client也是不透明的。

使用方法:

2. 客户端注册

在Client使用服务提供商提供的登录服务之前,需要在服务提供商上注册Client应用,具体注册方法OAuth2.0不作要求,由实现者自行决定。

但是注册时Client需要提供:

  1. Client类型
  2. Client重定向URI
  3. 其它任何授权服务器需要的信息

2.1 客户端类型

OAuth定义两种Client类型:

  1. confidential:能够维护其凭证机密性的客户端(例如,在对客户端凭证的访问受限的安全服务器上实现的客户端)
  2. public:无法维护其凭证机密性的客户端(例如,在资源所有者使用的设备上执行的客户端,例如安装的本地应用程序或基于web浏览器的应用程序)

客户端类型指定基于授权服务器对安全认证的定义及其可接受的客户端凭证暴露级别。授权服务器不应对客户端类型做出假设。

一般实现中不要求Client类型

OAuth2.0是围绕以下Client设计的:

  1. web应用 web应用程序是在web服务器上运行的机密客户端。资源所有者通过呈现在其用户代理(即浏览器)上的由Client提供的UI来访问Client。客户端凭据以及发给客户端的任何访问令牌都存储在web服务器上,并且不向资源所有者公开或由资源所有者访问。
  2. user-agent-based应用 基于用户代理的应用是公共客户端,其中客户端代码从web服务器下载并在资源所有者的用户代理(例如,web浏览器)内执行。协议数据和凭证对资源所有者来说很容易访问(并且经常可见)。由于此类应用程序驻留在用户代理中,因此它们可以在请求授权时无缝使用用户代理功能。
  3. native应用 本地应用是公共客户端,它安装和运行在资源拥有者的设备上。协议数据和凭证对于资源拥有着是可获取的。动态颁发的凭证,如access token,有可接受的保护水平。至少这些凭证不会被应用要交互的其它敌对服务器所获取。在某些平台上,这些凭据能够被其它防护应用保护。

2.2 客户端标识

Client在服务提供商上注册后,会被颁发一个客户端标识符——一个由Client提供的注册信息所生成的唯一字符串。客户端标识符不是秘钥,它被暴露在资源拥有着面前,而且不能单独用作Client认证。每个Client在每个服务提供商的标识符是唯一的。

2.3 客户端认证

如果客户端类型是机密的,则客户端和授权服务器建立适合于授权服务器的安全要求的客户端认证方法。授权服务器可以接受满足其安全要求的任何形式的客户端认证请求。

机密客户端通常被颁发一组用于与授权服务器进行身份验证的客户端凭据(例如密码、公钥/私钥对)。

授权服务器可以与公共客户端建立客户端认证方法。然而,授权服务器不应该只依赖公共客户端身份验证来认证公共客户端。

2.3.1客户端密码认证

拥有Client Password的客户端应该使用RFC2617中定义的HTTP Basic认证方式与授权服务器进行认证。即客户端标识符作为username,将username:password使用"application/x-www-form-urlencoded"编码,并添加到Authorization请求头中。如:Authorization: Basic czZCaGRSa3F0Mzo3RmpmcDBaQnIxS3REUmJuZlZkbUl3。

或者,授权服务器可以提供接受request-body的认证接口,request-body包含:

  1. client_id
  2. client_serect

虽然OAuth标准不建议使用第二种方案认证,但是真实场景中并不使用Basic认证方式,例如github的OAuth实现就是使用第二种方式,但是使用时绝对不能让秘钥暴露在URI中。

3. 协议端点

授权过程中涉及两个授权服务器端点:

  1. Authorization EndPoint:Client通过用户代理重定向到此端点,依此来获取授权
  2. Token EndPoint:Client携带授权许访问此端点,以交换access token,此端点需要认证客户端。

和一个客户端端点:

  1. Redirection EndPoint:授权服务器通过用户代理重定向到此端点,以此向客户端返回包含授权凭证的响应

3.1 授权端点

授权端点用于与资源拥有者交互并且获得其对Client的授权许可。但在此之前,授权服务器需要先验证资源拥有者的身份(通过用户名、密码登录,或session cookie等)。

获取授权端点的访问地址的方式OAuth2.0不作规范,但是授权端点地址通常会在服务提供商的文档中给出。

由于授权端点的访问会导致用户认证和密码的明文传输,所以授权服务器必须使用TLS(也就是授权服务器必须使用HTTPS)

授权服务器必须支持GET方式请求,也应该支持POST方式请求。

我的理解是,授权端点包括授权页面和授权接口。

对端点的GET请求返回授权页面,资源拥有者在授权页面同意授权后,对端点进行POST请求以生成正真的授权。

授权端点的参数:

  1. client_id: 在lattice中注册应用时生成的id
  2. redirect_uri:注册应用时指定的callback URL
  3. response_type:授权类型 code为授权码类型
  4. scope:需要授权范围
  5. state: client server生成的随机状态码

3.1.1 Response Type

授权端点只在授权码模式和隐含模式流程中使用。Client使用以下参数告知授权服务器自己想要的授权类型:

  • response_type:参数值为"code"时,授权类型为授权码;为"token"时,授权类型为隐含模式。参数值也可以是其它的扩展值。

如果授权请求没有携带respone_type参数,或者参数值不受授权服务器支持时,授权服务器必须返回一个如4.1.2.1的错误响应

3.1.2 Redirection EndPoint

在授权服务器完成与资源拥有者的交互后,授权服务器会将用户引导回Client(也就是返回302响应,重定向到Client提供的UI)。

重定向的URI是在Client向服务提供商注册自身时提供的URI,或者是在Client引导资源拥有者执行授权请求时所指定的redirect_uri参数中的值。

3.1.2.1 端点请求保密

当重定向请求中包含敏感信息时,重定向端点必须使用TLS,也就是要使用HTTPS。

但是由于使用HTTPS对于大部分Client开发者来说有一些障碍(证书贵,免费的证书没有完整的授权链路等),所以如果Client不使用HTTPS,则授权服务器必须在重定向之前提醒资源拥有着重定向端点的不安全性。

3.1.2.2 注册要求

授权服务器必须要求以下类型的Client注册他们的重定向端点:

  1. 所有的Public Clients
  2. 使用隐含模式的Confidential Clients

也就是说,在Client在向服务提供商注册自身时必须提供重定向端点。

服务提供商应要求Client提供完整的重定向URI, If requiring the registration of the complete redirection URI is not possible, the authorization server SHOULD require the registration of the URI scheme, authority, and path (allowing the client to dynamically vary only the query component of the redirection URI when requesting authorization)。我的理解是,如果不能注册完整的URI,则应该注册URI的部分结构,让Client在请求授权时指定redirect_uri时,只能变更相应的查询参数。

比如,如果Client注册的是https://example.client/index?custom_arg=12,则在Client请求授权时指定的redirect_uri的值可以是:

https://example.client/index?custom_arg=1&append_arg=123https://example.client/index/callback?custom_arg=1&append_arg=123等。也就是说注册时的URI格式必须包含在redirect_uri中。

服务提供商应该允许注册多个重定向端点。

如果不要求注册重定向端点,会导致攻击者将授权服务器用做开放的重定向器。也就是发送授权请求时redirect_uri不是已注册Client的重定向端点,而服务提供商没有要求Client注册重定向端点,则这个redirect_uri无从验证。

3.1.2.3 动态配置

如果Client在服务提供商中注册了多个重定向端点,或只注册了重定向端点的部分,或没有注册重定向端点,则Client在发起授权请求时必须带上redirect_uri参数。

当Client在授权请求中提供了redirect_uri,授权服务器必须将其值与已注册的重定向URI进行比较,确保其有效性。如果没有注册重定向URI,那么没办法,只能直接使用这个URI。

3.1.2.4 无效端点

如果授权服务器发现授权请求中的重定向URI无效,授权服务器必须提醒用户,并且不能自动重定向到这个无效URI。

3.1.2.5 端点内容

通常,对重定向端点的请求最终都会返回HTML响应。如果HTML响应直接由重定向端点返回,任何在这个HTML文档上的脚本都能读这个取重定向URI,即能获取授权凭证。

因此,重定向端点的响应内容不应该包含第三方脚本。

为了防止第三方脚本读取凭证:

  1. Client要让自身的脚本把URI中的授权凭证提取出来,也就是重写URI,要确保Client脚本在第三方脚本之前执行。
  2. 或者,重定向端点应该从其URI中提取出授权凭证,然后将用户代理重定向到另一个Client端点,重定向时授权凭证不能暴露在URI中。

3.2 Token端点

Client携带授权许可或refresh token来访问token端点以获取获取access token。除了隐含模式,任何授权类型都可以访问token端点(隐含模式直接颁发access token,没必要访问token端点)。

Client如何获取troken端点的URI本规范不作要求,但是服务提供商文档中一般会给出。token端点要使用post请求,且请求参数的Content-Type为application/x-www-form-urlencoded。token端点必须使用HTTPS。

3.2.1 Client认证

当请求Token端点时,保密Client或其它被颁发凭据的客户端必须与授权服务器进行身份认证。Client认证是为了:

  1. 强制将refresh token和授权码绑定到它们被颁发的Client。当授权码通过不安全的通道传递到重定向端点或重定向端点没有注册时,Client认证非常由必要。
  2. 当Client泄漏时,防止攻击者滥用盗取的refresh token,并且可以通过改变客户端凭据来恢复客户端的保密性。且更改客户端凭据比警用刷新令牌快的多。
  3. 为了实现认证管理,服务提供商会定期刷新Client凭据。刷新Client凭据要比刷新refresh token更快。

Client应该在访问token端点时携带client_id参数来标识本身。且client_id参数可以用来防止授权码被替换而不自知。

3.3 Access Token作用域

授权端点和token端点允许Client通过"scope"请求参数指定要获取的access_token的作用域。相应的,授权服务器使用"scope"响应参数来告知Client它颁发的access_token的作用域。

scope参数的值是一个大小写敏感的以空格分隔的字符串列表。列表中的每个字符串都有授权服务器定义,每个字符串代表一个作用域。

基于授权服务器的策略和资源拥有者的指示,授权服务器应该忽略Client请求的部分作用域,也就是说,Client要求的作用域只起声明作用,真正要给予它的作用域主要依靠资源拥有着的授权。如果授权服务器颁发的access_token的作用域与Client要求的作用域不一样,授权服务器要在响应中带上scope参数来告知Client被授予的实际作用域。

如果Client请求token端点时没有指定scope参数,授权服务器可以:

  1. 使用默认的授权作用域处理请求
  2. 或认为scope无效而拒绝此请求

服务提供商应该在其文档中明确指出其支持的授权作用域或默认作用域。

4. 获取授权

为了获取access token,Client要从资源拥有者手中获取授权。授权以授权许可的形式表示,Client使用授权许可来获取access token。OAuth定义了四种授权许可类型:授权码,隐含授权,资源拥有者凭证和客户端凭据。同样只支持自定义的扩展授权类型。

4.1 授权码授权

授权码类型的许可被用来获取access token 和 refresh token,对保密客户端来说它是最佳的选择。

因为授权码授予是一个基于重定向的流程,Client必须具有与用户代理交互的能力,它也需要能够接收从授权码服务器发送的请求。

直白点就是Client要能够将用户代理重定向到授权端点,授权端点要能够将用户代理重定向到重定向端点。

授权码授权流程

图中的A、B、C步骤由于要经过用户代理,所以被分为了两部分。

A:Client使用302响应将资源拥有者的用户代理重定向到授权端点。

B:授权服务器认证用户,并确定资源拥有者是准许还是拒绝Client的授权请求

C:假设资源拥有者准许,授权服务器将资源拥有者的用户代理重定向到A步骤中指定的重定向端点URI,重定向端点URI的路径参数中将包含授权码和Client提供的state。 资源拥有者要准许授权,也必须要通过用户代理向授权服务器发送准许授权请求,也就是点击同意授权按钮时会发送这个请求,准许授权请求将返回302响应,用户代理籍此响应重定向

D:Client携带上一步中获得的授权码和重定向端点的URI去获取access token。 在这个请求中,授权服务器需要认证Client的身份。认证方法就是,Client在请求时,将请求授权时指定的redirect_uri(也就是A步骤指定的)也附加到请求参数中。

因为重定向端点的URI在注册Client时可以指定多个,所以才会在这个请求中要求给出redirect_uri参数

E:授权服务器对Client进行认证,验证授权码,确保redirect_uri参数值域步骤C中的重定向端点URI相同。如果这些验证通过,授权服务器返回access token,且选择性的也返回refresh token,这取决于授权服务器的策略。

4.1.1 授权请求

授权请求就是A步骤中302响应要发出的请求。Client通过将以下参数以"application/x-www-form-urlencoded"的形式添加到授权端点URI后来构造这个请求,参数为:

  1. response_type(REQUIRED):授权码模式中固定为“code”,它表示授权许可类型
  2. client_id(REQUIRED):向服务提供商注册Client后得到的client id,它标识这个授权请求是对哪个Client的授权
  3. redirect_uri(OPTIONAL):重定向端点URI,如果Client在向服务提供商注册自身时只提供了一个重定向端点URI,那么可以不用填;
  4. scope(OPTIONAL):Client申请的权限范围
  5. state(OPTIONAL BUT RECOMMENDED):Client用于维护授权端点与重定向端点之间状态的不透明值。此参数用于防止csrf攻击。

授权服务器在接收到授权请求时,必须保证所有的参数是有效且正确的。确认请求有效后,授权服务器将认证资源拥有者然后获取它的授权决定。当资源拥有者决定后,授权服务器通过302响应将用户代理重定向到redirect_uri。

授权服务器必须忽略不认识的请求参数。

请求示例

4.1.2 授权响应

授权服务器在获得资源拥有着的授权后要做两件事:颁发授权码给Client;将用户代理重定向到重定向端点。因为重定向端点属于Client,所以这两件事可以通过授权请求的302响应同时完成。

即授权响应可以将授权码与重定向端点URI结合。结合方式就是,将以下参数以"application/x-www-form-urlencoded"的形式添加到重定向端点URI,然后将结合体作为302响应的location响应头的值,参数为:

  1. code(REQUIRED):授权服务器生成的授权码,为了降低泄露它产生的风险,code通常只有10分钟的有效期,且只能被使用一次。如果一个code被重复使用,授权服务器应该拒绝请求并且将之前基于此code颁发的所有token都撤销。授权码与client id和重定向端点URI绑定
  2. state:如果授权请求中有这个参数,那么授权响应也必须包含它,且值与授权请求中的值保持一致。

Client必须忽略不认识的响应参数。

响应示例

4.1.2.1 错误响应

如果授权请求是由于重定向URI的缺失、无效或不匹配,或client id的缺失或无效而被认定为是无效的。授权服务器要以某种方式告知资源拥有者,且不能自动重定向到无效的redirect_uri。

如果授权请求是由于除上述两个请求参数外的其它参数错误和资源拥有者拒绝授权而被认定为失败,授权服务器应该将以下参数以"application/x-www-form-urlencoded"的形式添到重定向端点URI中来告知Client请求授权的原因,参数为:

  1. error(REQUIRED):错误代码,其值可以为:

    1. invalid_request:请求缺失必要参数、包含无效参数值、包含多个相同参数或包含其它畸形参数
    2. unauthorized_client:Client无权使用此方法获取授权代码
    3. access_denied:资源拥有着拒绝授权
    4. unsupported_response_type:授权服务器不支持使用此返回获取授权代码(用户代理不支持在Location响应头中包含Query参数时使用此代码)
    5. invalid_scope:授权请求的scope值无效、位置、或畸形
    6. server_error:授权服务器内部异常,阻止其完成请求(因为503HTTP状态代码不能通过302重定向来传递,所以需要此错误代码)
    7. temporarily_unavailable:由于授权服务器过载或维护,无法处理授权请求(也是与f一样的原因而需要此代码)
  2. error_description(OPTIONAL):错误描述信息,不能包含ASCII中%x20-21 / %x23-5B / %x5D-7E

  3. error_uri(OPTIONAL):关于错误信息解读的网页URI

  4. state:如果授权请求中有这个参数,那么授权响应也必须包含它,且值与授权请求中的值保持一致。

错误响应示例

4.1.3 Access Token请求

Client通过携带以下参数向Token端点发送请求,这些参数以UTF-8编码并包含在request body中,且请求的Content-Type为"application/x-www-form-urlencoded",参数为:

  1. grant_type(REQUIRED):授权码授权中,只能设置为"authorization_code"
  2. code(REQUIRED):获取到的授权码
  3. redirect_uri:如果授权请求中有此参数,则此参数必填,且值与授权请求中的值相同
  4. client_id:如果Client还没有与授权服务器进行认证,则必填。

虽然rfc6749说的是if the client is not authenticating with the authorization server as described in Section 3.2.1。但是client_id其实永远都要填,因为授权码与client id和重定向端点URI绑定

在请求token端点时,保密Client或其它被颁发凭据的客户端必须与授权服务器进行身份认证,认证方法参考2.3.1。

请求示例

4.1.4 Access Token响应

如果对token端点的请求有效且资源拥有者已对Client授权,那么授权服务器将给Client颁发access token(选择性颁发refresh token)具体响应在5.1;如果Client认证失败,则返回错误响应,具体响应在5.2

响应示例

4.2 隐含授权

隐含授权类型用于直接获取access token(不支持获取refresh token),对操作已知的重定向URI的公共客户端来说是最佳的选择。

隐含授权也是基于重定向的流程。不同于授权码授权将授权请求和access token请求分开,隐含授权的授权请求直接返回access token。

因为access token将附在重定向端点URI上返回,所以access token会暴露给资源拥有着和设备上的其它应用;因此隐含授权不包括Client认证,且不依赖资源拥有者的存在和重定向端点的注册。

隐含授权流程

(A)Client通过将资源拥有者的用户代理重定向到授权端点开始授权流程;重定向请求要包含client id、scope、state和redirect uri等参数

(B)授权服务器认证资源拥有者,并确定资源拥有者是准许还是拒绝Client的授权请求

(C)假设资源拥有者同意授权,授权服务器使用302响应将资源拥有者的用户代理重定向回Client,重定向URI为A步骤中指定的redirect uri,且重定向URI中将包含access token。

(D)资源拥有者的用户代理遵循收到的302响应来请求相应的Client资源(Client接收此请求的方法不应该接收请求参数),请求参数应由用户代理保存在本地。

就是说重定向端点接收请求时忽略参数,而用户代理保存参数的方法就是不管它,让他在URI中待着。

(E)Client资源返回一个能够处理完整重定向URI和提取access token的网页(也就是内嵌js的HTML文档)。

(F)用户代理执行Client资源返回的脚本来提取access token。

(G)用户代理将access token传递给Client。

  1. 授权请求

隐含授予的授权请求与授权码授予的授权请求大致一样,不同之处在于response_type的值必须为token

请求示例

4.2.2 授权响应

rfc6749将此节命名为access token response,个人觉得还是叫授权响应更好

隐含授予的授权请求的响应直接包含access token,所以当资源拥有者同意授权后,授权服务器直接给Client颁发access token(绝对对不能颁发refresh token),并将它以"application/x-www-form-urlencoded"格式编码然后附在重定向端点URI后作为参数。响应要携带的所有参数为:

  1. access_token(REQUIRED)
  2. token_type(REQUIRED):具体值参照7.1
  3. expires_in(RECOMMENDED):表示access token从颁发到过期的时间间隔,单位为秒。如果忽略它,那么授权服务器应该以其它方式指定过期时间,且应在服务提供商文档中给出默认值。
  4. scope:代表授权服务器颁发的access token的作用域,如果授权的作用域与Client请求的作用域相同,可以选择性填写;如果授权的作用域与Client请求的作用域不同,则必须填写
  5. state:如果授权请求中包含此参数,则必填,且值要与授权请求中的值保持一致。

成功响应示例

开发者应该注意:

某些用户代理可能不支持在在302响应的Location响应头中包含Query参数,所以可能需要使用其它方法来重定向到重定向端点。比如,同意授权后的响应返回一个HTML页面,页面中包含一个“continue”按钮,使用这个按钮来重定向到重定向端点。

4.2.2.1 错误响应

隐含授权的错误响应与授权码授权的错误响应一致。

错误响应示例

4.3 资源拥有者密码凭证授予

此授权类型在资源拥有者高度信任Client的场景中适用(例如:Client是操作系统,或Client是服务提供商旗下应用)。授权服务器应只在其它授权类型不可用时开启此授权类型,且启用此授权类型时要多加注意。

它适用于能够获取资源拥有者凭证(用户名和密码)的Client。也适用于将现有的、使用如HTTP Basic认证等直接认证规范的Client迁移到OAuth规范。迁移方法是通过将Client存储的凭证转换为access token。

授权流程

(A)资源拥有者向Client提供其凭证,也就是用户名和密码。

(B)Client携带资源拥有者凭证向token端点直接请求access token。请求时,Client要与授权服务器进行认证。

(C)授权服务器认证Client,并且验证资源拥有者凭证。如果凭据有效,则颁发access token

可以看出此授权类型只涉及token端点

4.3.1 授权请求和响应

Client获取资源拥有者的凭证的方法此规范不作要求。但Client使用凭证获取到access token后必须丢弃凭证。

补充

此授权类型的请求授权方式与其它类型不同。当用户想要登录Client时,Client提供的登录表单就是授权请求,提交表单发出的请求就是授权响应。

4.3.2 Access Token请求

Client应使用"application/x-www-form-urlencoded"格式将参数放在请求体中来请求token端点,也就是POST请求,参数如下:

  1. grant_type(REQUIRED):此授权类型下,必须设置为"password"
  2. username(REQUIRED):资源拥有者用户名
  3. password(REQUIRED):资源拥有者密码
  4. scope(OPTIONAL):Client想要的授权作用域

如果Client是保密客户端或者被颁发了凭据,Client就必须与授权服务器进行认证,认证方法参考2.3.1。

请求示例

token端点收到请求后必须:

  1. 对保密Client和被颁发其它凭证的Client进行认证
  2. 验证资源拥有者凭证

4.3.3 Access Token响应

如果请求有效且所有认证通过,授权服务器要颁发如5.1所述的access token(选择性颁发refresh token)。

反之,授权服务器返回如5.2所述的错误响应。

成功响应示例

4.4 Client凭据授权

当Client想要访问在它自己掌握下的资源,或者那些已由资源拥有者授权给Client访问的资源时,Client只需要使用其自身的凭证来获取access_token。

只有保密客户端才能使用Client凭据授权

授权流程

(A)Client携带自身凭证请求token端点以获取access token。

(B)授权服务器对Client进行认证。如果验证通过,则颁发access token。

4.4.1 授权请求和响应

因为Client认证本身就可以用做权限授予,所以不需要额外的授权请求。

4.4.2 Access Token请求

Client将请求参数以"application/x-www-form-urlencoded"编码并放在请求体中来请求token端点以获取access token,参数为:

  1. grant_type(REQUIRED):Client凭据授权中,此参数值必须为"client_credentials"
  2. scope(OPTIONAL):Client请求授权的作用域

授权服务器必须与Client进行认证,认证方法参考2.3.1。

请求示例

4.4.3 Access Token响应

如果对token端点的请求有效且Client认证通过,则颁发如5.1所述的access token,但是不能颁发refresh token

否则,授权服务器返回如5.2所述的错误响应。

请求示例

4.5 扩展授权

5. 颁发Access Token

如果对token端点的请求有效且Client认证通过,则颁发如5.1所述的access token,选择性颁发refresh token。

否则,授权服务器返回如5.2所述的错误响应。

5.1 成功响应

成功响应(200)包含以下响应参数:

  1. access_token(REQUIRED):授权服务器颁发的access token
  2. token_type(REQUIRED):具体值参照7.1
  3. expires_in(RECOMMENDED):表示access token从颁发到过期的时间间隔,单位为秒。如果忽略它,那么授权服务器应该以其它方式指定过期时间,且应在服务提供商文档中给出默认值。
  4. refresh_token:授权码收授权和资源拥有者密码凭证授权选填,Client授权和隐含授权不填
  5. scope:代表授权服务器颁发的access token的作用域。在授权码收钱中不要写,因为在其授权请求与响应阶段就已经确定了作用域。对于其它授权类型,如果授权的作用域与Client请求的作用域相同,可以选择性填写;如果授权的作用域与Client请求的作用域不同,则必须填写。

这些响应参数应该放在响应体中,且媒体类型为"application/json"。

如果授权服务器的响应包含任何token、凭证或者其它敏感信息时,必须将"Cache-Control"响应头设置为"no-store",且将"Pragma"响应头设置为"no-cache"。

成功响应示例

Client必须忽略响应中不认识的参数。在服务提供商文档中应该给出任何授权服务器返回的任何值得长度和格式。

5.2 错误响应

错误响应(400)包含以下参数:

  1. error(REQUIRED):错误代码,其值可以为:

    1. invalid_request:请求缺失必要参数、包含无效参数值、包含多个相同参数或包含其它畸形参数
    2. invalid_client:Client认证失败。The authorization server MAY return an HTTP 401 (Unauthorized) status code to indicate which HTTP authentication schemes are supported. If the client attempted to authenticate via the “Authorization” request header field, the authorization server MUST respond with an HTTP 401 (Unauthorized) status code and include the “WWW-Authenticate” response header field matching the authentication scheme used by the client。 这个解释还是用原文比较好理解。
    3. invalid_grant:提供的授权许可或refresh token无效、过期或被撤销,或者它们与redirect uri不匹配。
    4. unauthorized_client:Client无权使用此授权类型获取授权代码
    5. unsupported_grant_type:授权服务器不支持此授权类型
    6. invalid_scope:授权请求的scope值无效、位置、或畸形
  2. error_description(OPTIONAL):错误描述信息,不能包含ASCII中%x20-21 / %x23-5B / %x5D-7E

  3. error_uri(OPTIONAL):关于错误信息解读的网页URI

这些响应参数应该放在响应体中,且媒体类型为"application/json"。

错误响应示例

6. 刷新Access Token

如果授权服务器给Client颁发了refresh token,Client将请求参数以"application/x-www-form-urlencoded"格式编码并添加到请求体中来向token端点发送刷新请求,请求参数为:

  1. grant_type(REQUIRED):值必须是"refresh_token"
  2. refresh_token(REQUIRED):颁发给Client的refresh token
  3. scope(OPTIONAL):请求的授权作用域。scope的值不得包含资源拥有者最初未授予Client的作用域,若果省略scope,那么默认其值为资源拥有者最初授予Client的作用域。

因为refresh token是长期凭证,所以它会被绑定到它被颁发的Client。如果Client是保密的或被颁发了凭证,Client必须与授权服务器进行认证,认证方法如3.2.1所述。

请求示例

收到请求后,授权服务器必须:

  1. 认证保密Client和被颁发了凭证的Client
  2. 确保refresh token是被颁发为认证Client的
  3. 验证refresh token

如果认证成功且验证通过,授权服务器将颁发access token给Client。如果请求无效或没通过验证,授权服务器返回如5.2的错误响应。

授权服务器也要颁发新的refresh token给Client以替换旧的,然后撤销Client与旧refresh token的绑定,并将新refresh token与Client绑定。新refresh token的作用域要与旧的一样。

7. 访问受保护资源

Client携带access token以访问受保护资源。资源服务器必须验证access token,确保它没有过期,并且确保它的作用域包含要请求的资源。资源服务器验证access token的方法本规范不做要求,但是通常包含资源服务器与授权服务器的交互和协作。

Client利用access token向资源服务器进行认证的方法取决于授权服务器颁发的access token的类型。

7.1 Access Token类型

Access token类型为Client提供了能够成功利用access token访问受保护资源的必要信息,Client不应在不知道access token的类型下使用它。

具体的Access Token类型用例,参照RFC6759

我们一般使用的类型为Bearer类型。

示例请求

Authorization请求头中,Bearer为access token类型,后面的字符串为access token

每个access token类型定义指定了授权服务器颁发给Client access token时要返回的额外参数(如果存在)。同样也定义了Client访问受保护资源时使用的认证方法。

7.2 错误响应

如果资源访问请求失败。资源服务器应该告知Client失败的原因。虽然错误的响应规范超出了本文档的范围,但是总的来说,一个错误响应选择应该包含以下信息以与本规范其它错误响应对应:

  1. error_description
  2. error_uri

结尾

8到11的内容为补充内容,需要可自行查看。

最后附上我自实现的授权码授权的示例流程

自实现OAuth2.0授权码授权流程

# 1. 简介

在传统的CS认证模型中,客户端通过使用资源拥有者(也就是用户)的凭证去请求服务器上的受限制资源。

客户端不是浏览器,浏览器是用户代理,在HTTP请求头中User-Agent指出的浏览器的版本、内核,而User-Agent是用户代理的意思,即浏览器帮助用户与服务器通信。真正的客户端是可以代表用户操作的任何应用,如应用的前端网页,Windows的桌面应用等等。

用户输入密码(即给予浏览器用户的凭证),让client替用户通过浏览器去访问用户在服务器上的受保护资源并呈现。

就如Basic认证,把“用户名+冒号+密码”用BASE64加密后,放到Authrization请求头中发送给服务器,每次请求都得加上Authorization请求头,需要配合HTTPS使用。

为了让第三方应用访问我们在服务器上受保护的资源,Resource Owner(用户)不得不将自己的凭证交给第三方应用,这就会产生了一些问题和限制:

  1. 第三方应用必须存储Resource Owner的凭证用以在将来访问时使用,即存储客户的明文密码。(非常不安全
  2. 服务器必去支持密码认证,尽管密码固有的安全弱点。(没理解,但可能是想表达用户已经将密码交出,密码认证不再有安全性可言)
  3. Resource Owner没有任何手段去限制第三方应用的访问时间,或者限制其访问范围;这导致第三方应用获得过大的访问权限。
  4. Resource Owner想要撤销某个第三方应用的访问权限时,会撤销所有第三方应用的访问权限,并且只能通过修改密码的方式撤销。
  5. 任何一个第三方应用泄露凭证(密码)都会造成用户的密码和所有受这个密码保护的资源被泄露。

OAuth通过引入一个授权层解决这些问题,并将客户端的角色与Resource Owner角色分离(传统的CS认证中,与服务器直接通信的是Client,也就是浏览器,用户与客户端角色模棱两可)。

在OAuth中,客户端被颁发一组与用户凭证完全不同的凭证(access_token),这个凭证是一个字符串,它包括访问范围,过期时间和其它与访问相关的属性。

如果用户同意授权给第三方应用,认证服务器会将access_token颁发给第三方应用客户端。第三方客户端籍此访问资源服务器托管的受保护资源。

1.1 角色

OAuth定义了四种角色:

  1. Resource Owner:能够授权访问受保护资源的实体。如果Resource Owner是一个人,那么它也叫最终用户
  2. Resource Server:托管受保护资源的服务器,能够使用access token处理对受保护资源的请求
  3. Client:代表Resource Owner访问受保护资源且有其授权的任何应用。
  4. Authorization Server:成功认证Resource Owner,并且Client获得用户授权后,Authorization Server向Client颁发access token。

Resource Server与Authorization Server可以是同一个服务,他们合称为服务提供商,Authorization Server颁发的access token能够被多个Resource Server接受。

1.2 协议流程

(A) Client向Resource Owner请求授权。授权请求可以由Client直接向Resource Owner发出,或者最好通过授权服务器间接发出(即通过重定向到Authorization Server授权页)。推荐后者

(B) Client收到Resource Owner的授权许可,授权许可的类型是此规范中定义的四种授权类型之一或者是一个扩展授权类型。授权类型取决于Client请求授权的方法和Authorization Server支持的授权类型。

© Client通过向授权服务器进行认证,且出示授权许可来请求access token。

(D) Authorization Server鉴定客户端并且验证授权许可,如果有效,则颁发access token

(E) Client通过access_token请求Resource Server上的受保护资源

(F) Resource Server鉴定access token,如果有效,处理此次请求

1.3 授权许可类型

授权许可是一个象征资源拥有者授权的凭证,它被Client用来获取access token。OAuth2.0规范定义四个授权类型,也支持自定义授权类型。

1.3.1 Authorization code

授权码是从授权服务器获取的,授权服务器作为Client和资源拥有者之间的中介。

Client引导资源拥有者访问授权服务器来发起授权,授权服务器引导用户携带授权码返回Client之前,会认证资源拥有并生成授权码。因此用户的凭证并不会与Client分享。之后使用授权码请求access token。

上图GitHub按钮就是Client引导资源拥有者的方式,鼠标放上去时,浏览器坐下角的地址为

此请求返回302响应

重定向地址为

结果为

当点击同意授权时,会请求

请求体为

1.3.2 Implicit

隐含模式是授权码模式的简化。隐含模式在用户授权后直接给Client颁发access token。

隐含模式颁发access token时不会鉴定Client,在某些情况下,可以使用redirect url来验证Client

1.3.3 Resource owner password credentials

资源拥有者密码凭证可以直接用做授权许可来获取access token。此模式,在资源拥有着与Client之间高度信任(例如用支付宝账号密码登录淘宝),或其它授权类型不可用时可用。

1.3.4 Client credentials

客户端凭证模式适用于Client要访问的受保护资源本就在Client的控制下时,即客户端为自己行事(客户端也是资源拥有者)

1.4 Access Token

Access token是象征授权的一个字符串,用于访问受保护资源,它对于Client是不透明的。

1.5 Refresh Token

Refresh Token是Access Token过期、无效时获取新的access token是的凭证。它也有Authorization Server颁发,且与Access Token同时颁发。它对于Client也是不透明的。

使用方法:

2. 客户端注册

在Client使用服务提供商提供的登录服务之前,需要在服务提供商上注册Client应用,具体注册方法OAuth2.0不作要求,由实现者自行决定。

但是注册时Client需要提供:

  1. Client类型
  2. Client重定向URI
  3. 其它任何授权服务器需要的信息

2.1 客户端类型

OAuth定义两种Client类型:

  1. confidential:能够维护其凭证机密性的客户端(例如,在对客户端凭证的访问受限的安全服务器上实现的客户端)
  2. public:无法维护其凭证机密性的客户端(例如,在资源所有者使用的设备上执行的客户端,例如安装的本地应用程序或基于web浏览器的应用程序)

客户端类型指定基于授权服务器对安全认证的定义及其可接受的客户端凭证暴露级别。授权服务器不应对客户端类型做出假设。

一般实现中不要求Client类型

OAuth2.0是围绕以下Client设计的:

  1. web应用 web应用程序是在web服务器上运行的机密客户端。资源所有者通过呈现在其用户代理(即浏览器)上的由Client提供的UI来访问Client。客户端凭据以及发给客户端的任何访问令牌都存储在web服务器上,并且不向资源所有者公开或由资源所有者访问。
  2. user-agent-based应用 基于用户代理的应用是公共客户端,其中客户端代码从web服务器下载并在资源所有者的用户代理(例如,web浏览器)内执行。协议数据和凭证对资源所有者来说很容易访问(并且经常可见)。由于此类应用程序驻留在用户代理中,因此它们可以在请求授权时无缝使用用户代理功能。
  3. native应用 本地应用是公共客户端,它安装和运行在资源拥有者的设备上。协议数据和凭证对于资源拥有着是可获取的。动态颁发的凭证,如access token,有可接受的保护水平。至少这些凭证不会被应用要交互的其它敌对服务器所获取。在某些平台上,这些凭据能够被其它防护应用保护。

2.2 客户端标识

Client在服务提供商上注册后,会被颁发一个客户端标识符——一个由Client提供的注册信息所生成的唯一字符串。客户端标识符不是秘钥,它被暴露在资源拥有着面前,而且不能单独用作Client认证。每个Client在每个服务提供商的标识符是唯一的。

2.3 客户端认证

如果客户端类型是机密的,则客户端和授权服务器建立适合于授权服务器的安全要求的客户端认证方法。授权服务器可以接受满足其安全要求的任何形式的客户端认证请求。

机密客户端通常被颁发一组用于与授权服务器进行身份验证的客户端凭据(例如密码、公钥/私钥对)。

授权服务器可以与公共客户端建立客户端认证方法。然而,授权服务器不应该只依赖公共客户端身份验证来认证公共客户端。

2.3.1客户端密码认证

拥有Client Password的客户端应该使用RFC2617中定义的HTTP Basic认证方式与授权服务器进行认证。即客户端标识符作为username,将username:password使用"application/x-www-form-urlencoded"编码,并添加到Authorization请求头中。如:Authorization: Basic czZCaGRSa3F0Mzo3RmpmcDBaQnIxS3REUmJuZlZkbUl3。

或者,授权服务器可以提供接受request-body的认证接口,request-body包含:

  1. client_id
  2. client_serect

虽然OAuth标准不建议使用第二种方案认证,但是真实场景中并不使用Basic认证方式,例如github的OAuth实现就是使用第二种方式,但是使用时绝对不能让秘钥暴露在URI中。

3. 协议端点

授权过程中涉及两个授权服务器端点:

  1. Authorization EndPoint:Client通过用户代理重定向到此端点,依此来获取授权
  2. Token EndPoint:Client携带授权许访问此端点,以交换access token,此端点需要认证客户端。

和一个客户端端点:

  1. Redirection EndPoint:授权服务器通过用户代理重定向到此端点,以此向客户端返回包含授权凭证的响应

3.1 授权端点

授权端点用于与资源拥有者交互并且获得其对Client的授权许可。但在此之前,授权服务器需要先验证资源拥有者的身份(通过用户名、密码登录,或session cookie等)。

获取授权端点的访问地址的方式OAuth2.0不作规范,但是授权端点地址通常会在服务提供商的文档中给出。

由于授权端点的访问会导致用户认证和密码的明文传输,所以授权服务器必须使用TLS(也就是授权服务器必须使用HTTPS)

授权服务器必须支持GET方式请求,也应该支持POST方式请求。

我的理解是,授权端点包括授权页面和授权接口。

对端点的GET请求返回授权页面,资源拥有者在授权页面同意授权后,对端点进行POST请求以生成正真的授权。

授权端点的参数:

  1. client_id: 在lattice中注册应用时生成的id
  2. redirect_uri:注册应用时指定的callback URL
  3. response_type:授权类型 code为授权码类型
  4. scope:需要授权范围
  5. state: client server生成的随机状态码

3.1.1 Response Type

授权端点只在授权码模式和隐含模式流程中使用。Client使用以下参数告知授权服务器自己想要的授权类型:

  • response_type:参数值为"code"时,授权类型为授权码;为"token"时,授权类型为隐含模式。参数值也可以是其它的扩展值。

如果授权请求没有携带respone_type参数,或者参数值不受授权服务器支持时,授权服务器必须返回一个如4.1.2.1的错误响应

3.1.2 Redirection EndPoint

在授权服务器完成与资源拥有者的交互后,授权服务器会将用户引导回Client(也就是返回302响应,重定向到Client提供的UI)。

重定向的URI是在Client向服务提供商注册自身时提供的URI,或者是在Client引导资源拥有者执行授权请求时所指定的redirect_uri参数中的值。

3.1.2.1 端点请求保密

当重定向请求中包含敏感信息时,重定向端点必须使用TLS,也就是要使用HTTPS。

但是由于使用HTTPS对于大部分Client开发者来说有一些障碍(证书贵,免费的证书没有完整的授权链路等),所以如果Client不使用HTTPS,则授权服务器必须在重定向之前提醒资源拥有着重定向端点的不安全性。

3.1.2.2 注册要求

授权服务器必须要求以下类型的Client注册他们的重定向端点:

  1. 所有的Public Clients
  2. 使用隐含模式的Confidential Clients

也就是说,在Client在向服务提供商注册自身时必须提供重定向端点。

服务提供商应要求Client提供完整的重定向URI, If requiring the registration of the complete redirection URI is not possible, the authorization server SHOULD require the registration of the URI scheme, authority, and path (allowing the client to dynamically vary only the query component of the redirection URI when requesting authorization)。我的理解是,如果不能注册完整的URI,则应该注册URI的部分结构,让Client在请求授权时指定redirect_uri时,只能变更相应的查询参数。

比如,如果Client注册的是https://example.client/index?custom_arg=12,则在Client请求授权时指定的redirect_uri的值可以是:

https://example.client/index?custom_arg=1&append_arg=123https://example.client/index/callback?custom_arg=1&append_arg=123等。也就是说注册时的URI格式必须包含在redirect_uri中。

服务提供商应该允许注册多个重定向端点。

如果不要求注册重定向端点,会导致攻击者将授权服务器用做开放的重定向器。也就是发送授权请求时redirect_uri不是已注册Client的重定向端点,而服务提供商没有要求Client注册重定向端点,则这个redirect_uri无从验证。

3.1.2.3 动态配置

如果Client在服务提供商中注册了多个重定向端点,或只注册了重定向端点的部分,或没有注册重定向端点,则Client在发起授权请求时必须带上redirect_uri参数。

当Client在授权请求中提供了redirect_uri,授权服务器必须将其值与已注册的重定向URI进行比较,确保其有效性。如果没有注册重定向URI,那么没办法,只能直接使用这个URI。

3.1.2.4 无效端点

如果授权服务器发现授权请求中的重定向URI无效,授权服务器必须提醒用户,并且不能自动重定向到这个无效URI。

3.1.2.5 端点内容

通常,对重定向端点的请求最终都会返回HTML响应。如果HTML响应直接由重定向端点返回,任何在这个HTML文档上的脚本都能读这个取重定向URI,即能获取授权凭证。

因此,重定向端点的响应内容不应该包含第三方脚本。

为了防止第三方脚本读取凭证:

  1. Client要让自身的脚本把URI中的授权凭证提取出来,也就是重写URI,要确保Client脚本在第三方脚本之前执行。
  2. 或者,重定向端点应该从其URI中提取出授权凭证,然后将用户代理重定向到另一个Client端点,重定向时授权凭证不能暴露在URI中。

3.2 Token端点

Client携带授权许可或refresh token来访问token端点以获取获取access token。除了隐含模式,任何授权类型都可以访问token端点(隐含模式直接颁发access token,没必要访问token端点)。

Client如何获取troken端点的URI本规范不作要求,但是服务提供商文档中一般会给出。token端点要使用post请求,且请求参数的Content-Type为application/x-www-form-urlencoded。token端点必须使用HTTPS。

3.2.1 Client认证

当请求Token端点时,保密Client或其它被颁发凭据的客户端必须与授权服务器进行身份认证。Client认证是为了:

  1. 强制将refresh token和授权码绑定到它们被颁发的Client。当授权码通过不安全的通道传递到重定向端点或重定向端点没有注册时,Client认证非常由必要。
  2. 当Client泄漏时,防止攻击者滥用盗取的refresh token,并且可以通过改变客户端凭据来恢复客户端的保密性。且更改客户端凭据比警用刷新令牌快的多。
  3. 为了实现认证管理,服务提供商会定期刷新Client凭据。刷新Client凭据要比刷新refresh token更快。

Client应该在访问token端点时携带client_id参数来标识本身。且client_id参数可以用来防止授权码被替换而不自知。

3.3 Access Token作用域

授权端点和token端点允许Client通过"scope"请求参数指定要获取的access_token的作用域。相应的,授权服务器使用"scope"响应参数来告知Client它颁发的access_token的作用域。

scope参数的值是一个大小写敏感的以空格分隔的字符串列表。列表中的每个字符串都有授权服务器定义,每个字符串代表一个作用域。

基于授权服务器的策略和资源拥有者的指示,授权服务器应该忽略Client请求的部分作用域,也就是说,Client要求的作用域只起声明作用,真正要给予它的作用域主要依靠资源拥有着的授权。如果授权服务器颁发的access_token的作用域与Client要求的作用域不一样,授权服务器要在响应中带上scope参数来告知Client被授予的实际作用域。

如果Client请求token端点时没有指定scope参数,授权服务器可以:

  1. 使用默认的授权作用域处理请求
  2. 或认为scope无效而拒绝此请求

服务提供商应该在其文档中明确指出其支持的授权作用域或默认作用域。

4. 获取授权

为了获取access token,Client要从资源拥有者手中获取授权。授权以授权许可的形式表示,Client使用授权许可来获取access token。OAuth定义了四种授权许可类型:授权码,隐含授权,资源拥有者凭证和客户端凭据。同样只支持自定义的扩展授权类型。

4.1 授权码授权

授权码类型的许可被用来获取access token 和 refresh token,对保密客户端来说它是最佳的选择。

因为授权码授予是一个基于重定向的流程,Client必须具有与用户代理交互的能力,它也需要能够接收从授权码服务器发送的请求。

直白点就是Client要能够将用户代理重定向到授权端点,授权端点要能够将用户代理重定向到重定向端点。

授权码授权流程

图中的A、B、C步骤由于要经过用户代理,所以被分为了两部分。

A:Client使用302响应将资源拥有者的用户代理重定向到授权端点。

B:授权服务器认证用户,并确定资源拥有者是准许还是拒绝Client的授权请求

C:假设资源拥有者准许,授权服务器将资源拥有者的用户代理重定向到A步骤中指定的重定向端点URI,重定向端点URI的路径参数中将包含授权码和Client提供的state。 资源拥有者要准许授权,也必须要通过用户代理向授权服务器发送准许授权请求,也就是点击同意授权按钮时会发送这个请求,准许授权请求将返回302响应,用户代理籍此响应重定向

D:Client携带上一步中获得的授权码和重定向端点的URI去获取access token。 在这个请求中,授权服务器需要认证Client的身份。认证方法就是,Client在请求时,将请求授权时指定的redirect_uri(也就是A步骤指定的)也附加到请求参数中。

因为重定向端点的URI在注册Client时可以指定多个,所以才会在这个请求中要求给出redirect_uri参数

E:授权服务器对Client进行认证,验证授权码,确保redirect_uri参数值域步骤C中的重定向端点URI相同。如果这些验证通过,授权服务器返回access token,且选择性的也返回refresh token,这取决于授权服务器的策略。

4.1.1 授权请求

授权请求就是A步骤中302响应要发出的请求。Client通过将以下参数以"application/x-www-form-urlencoded"的形式添加到授权端点URI后来构造这个请求,参数为:

  1. response_type(REQUIRED):授权码模式中固定为“code”,它表示授权许可类型
  2. client_id(REQUIRED):向服务提供商注册Client后得到的client id,它标识这个授权请求是对哪个Client的授权
  3. redirect_uri(OPTIONAL):重定向端点URI,如果Client在向服务提供商注册自身时只提供了一个重定向端点URI,那么可以不用填;
  4. scope(OPTIONAL):Client申请的权限范围
  5. state(OPTIONAL BUT RECOMMENDED):Client用于维护授权端点与重定向端点之间状态的不透明值。此参数用于防止csrf攻击。

授权服务器在接收到授权请求时,必须保证所有的参数是有效且正确的。确认请求有效后,授权服务器将认证资源拥有者然后获取它的授权决定。当资源拥有者决定后,授权服务器通过302响应将用户代理重定向到redirect_uri。

授权服务器必须忽略不认识的请求参数。

请求示例

4.1.2 授权响应

授权服务器在获得资源拥有着的授权后要做两件事:颁发授权码给Client;将用户代理重定向到重定向端点。因为重定向端点属于Client,所以这两件事可以通过授权请求的302响应同时完成。

即授权响应可以将授权码与重定向端点URI结合。结合方式就是,将以下参数以"application/x-www-form-urlencoded"的形式添加到重定向端点URI,然后将结合体作为302响应的location响应头的值,参数为:

  1. code(REQUIRED):授权服务器生成的授权码,为了降低泄露它产生的风险,code通常只有10分钟的有效期,且只能被使用一次。如果一个code被重复使用,授权服务器应该拒绝请求并且将之前基于此code颁发的所有token都撤销。授权码与client id和重定向端点URI绑定
  2. state:如果授权请求中有这个参数,那么授权响应也必须包含它,且值与授权请求中的值保持一致。

Client必须忽略不认识的响应参数。

响应示例

4.1.2.1 错误响应

如果授权请求是由于重定向URI的缺失、无效或不匹配,或client id的缺失或无效而被认定为是无效的。授权服务器要以某种方式告知资源拥有者,且不能自动重定向到无效的redirect_uri。

如果授权请求是由于除上述两个请求参数外的其它参数错误和资源拥有者拒绝授权而被认定为失败,授权服务器应该将以下参数以"application/x-www-form-urlencoded"的形式添到重定向端点URI中来告知Client请求授权的原因,参数为:

  1. error(REQUIRED):错误代码,其值可以为:

    1. invalid_request:请求缺失必要参数、包含无效参数值、包含多个相同参数或包含其它畸形参数
    2. unauthorized_client:Client无权使用此方法获取授权代码
    3. access_denied:资源拥有着拒绝授权
    4. unsupported_response_type:授权服务器不支持使用此返回获取授权代码(用户代理不支持在Location响应头中包含Query参数时使用此代码)
    5. invalid_scope:授权请求的scope值无效、位置、或畸形
    6. server_error:授权服务器内部异常,阻止其完成请求(因为503HTTP状态代码不能通过302重定向来传递,所以需要此错误代码)
    7. temporarily_unavailable:由于授权服务器过载或维护,无法处理授权请求(也是与f一样的原因而需要此代码)
  2. error_description(OPTIONAL):错误描述信息,不能包含ASCII中%x20-21 / %x23-5B / %x5D-7E

  3. error_uri(OPTIONAL):关于错误信息解读的网页URI

  4. state:如果授权请求中有这个参数,那么授权响应也必须包含它,且值与授权请求中的值保持一致。

错误响应示例

4.1.3 Access Token请求

Client通过携带以下参数向Token端点发送请求,这些参数以UTF-8编码并包含在request body中,且请求的Content-Type为"application/x-www-form-urlencoded",参数为:

  1. grant_type(REQUIRED):授权码授权中,只能设置为"authorization_code"
  2. code(REQUIRED):获取到的授权码
  3. redirect_uri:如果授权请求中有此参数,则此参数必填,且值与授权请求中的值相同
  4. client_id:如果Client还没有与授权服务器进行认证,则必填。

虽然rfc6749说的是if the client is not authenticating with the authorization server as described in Section 3.2.1。但是client_id其实永远都要填,因为授权码与client id和重定向端点URI绑定

在请求token端点时,保密Client或其它被颁发凭据的客户端必须与授权服务器进行身份认证,认证方法参考2.3.1。

请求示例

4.1.4 Access Token响应

如果对token端点的请求有效且资源拥有者已对Client授权,那么授权服务器将给Client颁发access token(选择性颁发refresh token)具体响应在5.1;如果Client认证失败,则返回错误响应,具体响应在5.2

响应示例

4.2 隐含授权

隐含授权类型用于直接获取access token(不支持获取refresh token),对操作已知的重定向URI的公共客户端来说是最佳的选择。

隐含授权也是基于重定向的流程。不同于授权码授权将授权请求和access token请求分开,隐含授权的授权请求直接返回access token。

因为access token将附在重定向端点URI上返回,所以access token会暴露给资源拥有着和设备上的其它应用;因此隐含授权不包括Client认证,且不依赖资源拥有者的存在和重定向端点的注册。

隐含授权流程

(A)Client通过将资源拥有者的用户代理重定向到授权端点开始授权流程;重定向请求要包含client id、scope、state和redirect uri等参数

(B)授权服务器认证资源拥有者,并确定资源拥有者是准许还是拒绝Client的授权请求

(C)假设资源拥有者同意授权,授权服务器使用302响应将资源拥有者的用户代理重定向回Client,重定向URI为A步骤中指定的redirect uri,且重定向URI中将包含access token。

(D)资源拥有者的用户代理遵循收到的302响应来请求相应的Client资源(Client接收此请求的方法不应该接收请求参数),请求参数应由用户代理保存在本地。

就是说重定向端点接收请求时忽略参数,而用户代理保存参数的方法就是不管它,让他在URI中待着。

(E)Client资源返回一个能够处理完整重定向URI和提取access token的网页(也就是内嵌js的HTML文档)。

(F)用户代理执行Client资源返回的脚本来提取access token。

(G)用户代理将access token传递给Client。

  1. 授权请求

隐含授予的授权请求与授权码授予的授权请求大致一样,不同之处在于response_type的值必须为token

请求示例

4.2.2 授权响应

rfc6749将此节命名为access token response,个人觉得还是叫授权响应更好

隐含授予的授权请求的响应直接包含access token,所以当资源拥有者同意授权后,授权服务器直接给Client颁发access token(绝对对不能颁发refresh token),并将它以"application/x-www-form-urlencoded"格式编码然后附在重定向端点URI后作为参数。响应要携带的所有参数为:

  1. access_token(REQUIRED)
  2. token_type(REQUIRED):具体值参照7.1
  3. expires_in(RECOMMENDED):表示access token从颁发到过期的时间间隔,单位为秒。如果忽略它,那么授权服务器应该以其它方式指定过期时间,且应在服务提供商文档中给出默认值。
  4. scope:代表授权服务器颁发的access token的作用域,如果授权的作用域与Client请求的作用域相同,可以选择性填写;如果授权的作用域与Client请求的作用域不同,则必须填写
  5. state:如果授权请求中包含此参数,则必填,且值要与授权请求中的值保持一致。

成功响应示例

开发者应该注意:

某些用户代理可能不支持在在302响应的Location响应头中包含Query参数,所以可能需要使用其它方法来重定向到重定向端点。比如,同意授权后的响应返回一个HTML页面,页面中包含一个“continue”按钮,使用这个按钮来重定向到重定向端点。

4.2.2.1 错误响应

隐含授权的错误响应与授权码授权的错误响应一致。

错误响应示例

4.3 资源拥有者密码凭证授予

此授权类型在资源拥有者高度信任Client的场景中适用(例如:Client是操作系统,或Client是服务提供商旗下应用)。授权服务器应只在其它授权类型不可用时开启此授权类型,且启用此授权类型时要多加注意。

它适用于能够获取资源拥有者凭证(用户名和密码)的Client。也适用于将现有的、使用如HTTP Basic认证等直接认证规范的Client迁移到OAuth规范。迁移方法是通过将Client存储的凭证转换为access token。

授权流程

(A)资源拥有者向Client提供其凭证,也就是用户名和密码。

(B)Client携带资源拥有者凭证向token端点直接请求access token。请求时,Client要与授权服务器进行认证。

(C)授权服务器认证Client,并且验证资源拥有者凭证。如果凭据有效,则颁发access token

可以看出此授权类型只涉及token端点

4.3.1 授权请求和响应

Client获取资源拥有者的凭证的方法此规范不作要求。但Client使用凭证获取到access token后必须丢弃凭证。

补充

此授权类型的请求授权方式与其它类型不同。当用户想要登录Client时,Client提供的登录表单就是授权请求,提交表单发出的请求就是授权响应。

4.3.2 Access Token请求

Client应使用"application/x-www-form-urlencoded"格式将参数放在请求体中来请求token端点,也就是POST请求,参数如下:

  1. grant_type(REQUIRED):此授权类型下,必须设置为"password"
  2. username(REQUIRED):资源拥有者用户名
  3. password(REQUIRED):资源拥有者密码
  4. scope(OPTIONAL):Client想要的授权作用域

如果Client是保密客户端或者被颁发了凭据,Client就必须与授权服务器进行认证,认证方法参考2.3.1。

请求示例

token端点收到请求后必须:

  1. 对保密Client和被颁发其它凭证的Client进行认证
  2. 验证资源拥有者凭证

4.3.3 Access Token响应

如果请求有效且所有认证通过,授权服务器要颁发如5.1所述的access token(选择性颁发refresh token)。

反之,授权服务器返回如5.2所述的错误响应。

成功响应示例

4.4 Client凭据授权

当Client想要访问在它自己掌握下的资源,或者那些已由资源拥有者授权给Client访问的资源时,Client只需要使用其自身的凭证来获取access_token。

只有保密客户端才能使用Client凭据授权

授权流程

(A)Client携带自身凭证请求token端点以获取access token。

(B)授权服务器对Client进行认证。如果验证通过,则颁发access token。

4.4.1 授权请求和响应

因为Client认证本身就可以用做权限授予,所以不需要额外的授权请求。

4.4.2 Access Token请求

Client将请求参数以"application/x-www-form-urlencoded"编码并放在请求体中来请求token端点以获取access token,参数为:

  1. grant_type(REQUIRED):Client凭据授权中,此参数值必须为"client_credentials"
  2. scope(OPTIONAL):Client请求授权的作用域

授权服务器必须与Client进行认证,认证方法参考2.3.1。

请求示例

4.4.3 Access Token响应

如果对token端点的请求有效且Client认证通过,则颁发如5.1所述的access token,但是不能颁发refresh token

否则,授权服务器返回如5.2所述的错误响应。

请求示例

4.5 扩展授权

5. 颁发Access Token

如果对token端点的请求有效且Client认证通过,则颁发如5.1所述的access token,选择性颁发refresh token。

否则,授权服务器返回如5.2所述的错误响应。

5.1 成功响应

成功响应(200)包含以下响应参数:

  1. access_token(REQUIRED):授权服务器颁发的access token
  2. token_type(REQUIRED):具体值参照7.1
  3. expires_in(RECOMMENDED):表示access token从颁发到过期的时间间隔,单位为秒。如果忽略它,那么授权服务器应该以其它方式指定过期时间,且应在服务提供商文档中给出默认值。
  4. refresh_token:授权码收授权和资源拥有者密码凭证授权选填,Client授权和隐含授权不填
  5. scope:代表授权服务器颁发的access token的作用域。在授权码收钱中不要写,因为在其授权请求与响应阶段就已经确定了作用域。对于其它授权类型,如果授权的作用域与Client请求的作用域相同,可以选择性填写;如果授权的作用域与Client请求的作用域不同,则必须填写。

这些响应参数应该放在响应体中,且媒体类型为"application/json"。

如果授权服务器的响应包含任何token、凭证或者其它敏感信息时,必须将"Cache-Control"响应头设置为"no-store",且将"Pragma"响应头设置为"no-cache"。

成功响应示例

Client必须忽略响应中不认识的参数。在服务提供商文档中应该给出任何授权服务器返回的任何值得长度和格式。

5.2 错误响应

错误响应(400)包含以下参数:

  1. error(REQUIRED):错误代码,其值可以为:

    1. invalid_request:请求缺失必要参数、包含无效参数值、包含多个相同参数或包含其它畸形参数
    2. invalid_client:Client认证失败。The authorization server MAY return an HTTP 401 (Unauthorized) status code to indicate which HTTP authentication schemes are supported. If the client attempted to authenticate via the “Authorization” request header field, the authorization server MUST respond with an HTTP 401 (Unauthorized) status code and include the “WWW-Authenticate” response header field matching the authentication scheme used by the client。 这个解释还是用原文比较好理解。
    3. invalid_grant:提供的授权许可或refresh token无效、过期或被撤销,或者它们与redirect uri不匹配。
    4. unauthorized_client:Client无权使用此授权类型获取授权代码
    5. unsupported_grant_type:授权服务器不支持此授权类型
    6. invalid_scope:授权请求的scope值无效、位置、或畸形
  2. error_description(OPTIONAL):错误描述信息,不能包含ASCII中%x20-21 / %x23-5B / %x5D-7E

  3. error_uri(OPTIONAL):关于错误信息解读的网页URI

这些响应参数应该放在响应体中,且媒体类型为"application/json"。

错误响应示例

6. 刷新Access Token

如果授权服务器给Client颁发了refresh token,Client将请求参数以"application/x-www-form-urlencoded"格式编码并添加到请求体中来向token端点发送刷新请求,请求参数为:

  1. grant_type(REQUIRED):值必须是"refresh_token"
  2. refresh_token(REQUIRED):颁发给Client的refresh token
  3. scope(OPTIONAL):请求的授权作用域。scope的值不得包含资源拥有者最初未授予Client的作用域,若果省略scope,那么默认其值为资源拥有者最初授予Client的作用域。

因为refresh token是长期凭证,所以它会被绑定到它被颁发的Client。如果Client是保密的或被颁发了凭证,Client必须与授权服务器进行认证,认证方法如3.2.1所述。

请求示例

收到请求后,授权服务器必须:

  1. 认证保密Client和被颁发了凭证的Client
  2. 确保refresh token是被颁发为认证Client的
  3. 验证refresh token

如果认证成功且验证通过,授权服务器将颁发access token给Client。如果请求无效或没通过验证,授权服务器返回如5.2的错误响应。

授权服务器也要颁发新的refresh token给Client以替换旧的,然后撤销Client与旧refresh token的绑定,并将新refresh token与Client绑定。新refresh token的作用域要与旧的一样。

7. 访问受保护资源

Client携带access token以访问受保护资源。资源服务器必须验证access token,确保它没有过期,并且确保它的作用域包含要请求的资源。资源服务器验证access token的方法本规范不做要求,但是通常包含资源服务器与授权服务器的交互和协作。

Client利用access token向资源服务器进行认证的方法取决于授权服务器颁发的access token的类型。

7.1 Access Token类型

Access token类型为Client提供了能够成功利用access token访问受保护资源的必要信息,Client不应在不知道access token的类型下使用它。

具体的Access Token类型用例,参照RFC6759

我们一般使用的类型为Bearer类型。

示例请求

Authorization请求头中,Bearer为access token类型,后面的字符串为access token

每个access token类型定义指定了授权服务器颁发给Client access token时要返回的额外参数(如果存在)。同样也定义了Client访问受保护资源时使用的认证方法。

7.2 错误响应

如果资源访问请求失败。资源服务器应该告知Client失败的原因。虽然错误的响应规范超出了本文档的范围,但是总的来说,一个错误响应选择应该包含以下信息以与本规范其它错误响应对应:

  1. error_description
  2. error_uri

结尾

8到11的内容为补充内容,需要可自行查看。

最后附上我自实现的授权码授权的示例流程

自实现OAuth2.0授权码授权流程

RFC6749-OAuth2.0相关推荐

  1. Oauth2.0的安全运行是否必须使用Https协议?官方总结的安全问题有哪些?(附英文文档分析)

    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言 一.Oauth2.0 1.Oauth2.0与Https的关系? 2.Oauth2.0的user-agent不使用Htt ...

  2. 从协议入手,剖析OAuth2.0(译 RFC 6749)

    1.介绍       https://tools.ietf.org/html/rfc6749   传统的client-server授权模型,客户端通过使用凭证(通常的用户名和明文密码)访问服务端受保护 ...

  3. OAuth2.0 基础概述

    web:http://oauth.net/2/ rfc:http://tools.ietf.org/html/rfc6749 doc:http://oauth.net/documentation/ c ...

  4. oauth2 增加token 返回参数_RingCentral Tech | OAuth2.0面面观

    ▲点击"RingCentral铃盛软件"并设为[星标],及时获取RingCentral的资讯 只要是接触过开放各种开放平台的开发者,对于OAuth概念肯定不陌生.但是由于OAuth ...

  5. OAuth2.0学习(1-1)OAuth2.0是什么?

    目前很多开放平台如新浪微博开放平台都在使用提供开放API接口供开发者使用,随之带来了第三方应用要到开放平台进行授权的问题 OAuth就是用于为第三方应用授权访问用户的资源应用的. 目前有OAuth1. ...

  6. sso和oauth2.0的简单了解学习

    sso,单点登录,single sign on 缩写.sso多用于多个应用之间的切换,例如百度论坛.百度知道.百度云.百度文库等,在其中一个系统中登录,(登录有效期内)切换到另一个系统的时候,不必再次 ...

  7. OAuth2.0授权流程分析

    Oauth2认证流程 第三方认证技术方案最主要是解决认证协议的通用标准 问题,因为要实现 跨系统认证,各系统之间要遵循一定的接口协议. OAUTH协议为用户资源的授权提供了一个安全的.开放而又简易的标 ...

  8. OAuth2.0学习(2-1)Spring Security OAuth2.0 开发指南

    开发指南:http://www.cnblogs.com/xingxueliao/p/5911292.html Spring OAuth2.0 提供者实现原理: Spring OAuth2.0提供者实际 ...

  9. ASP.NET MVC使用Oauth2.0实现身份验证

    随着软件的不断发展,出现了更多的身份验证使用场景,除了典型的服务器与客户端之间的身份验证外还有,如服务与服务之间的(如微服务架构).服务器与多种客户端的(如PC.移动.Web等),甚至还有需要以服务的 ...

  10. OAuth2.0 原理流程及其单点登录和权限控制

    作者:王克锋 kefeng.wang/2018/04/06/oauth2-sso 单点登录是多域名企业站点流行的登录方式.本文以现实生活场景辅助理解,力争彻底理清 OAuth2.0 实现单点登录的原理 ...

最新文章

  1. canvas java 上传截图_在Vue项目中使用html2canvas生成页面截图并上传
  2. APR分析-共享内存篇
  3. 抵御物联网DDoS军团
  4. 前端代码有关搜索引擎的代码
  5. 计算机科学学院陈瑜,浙江大学城市学院计算机与计算科学学院 计算机科学与技术 陈则伦...
  6. html 自动适应手机屏幕大小,HTML5 canvas自适应手机屏幕大小的一种解决方案
  7. 热门项目:高精度图像分类全流程开发
  8. 越狱后必装软件_iOS 13全系统越狱详细教程疑难解答
  9. 商业认知,新的一年已经开始,许多老板都制订了新的目标
  10. 中国基于散射的光学传感器行业市场供需与战略研究报告
  11. 【HDU 6299】Balanced Sequence
  12. 卡夫卡(kafka)详解
  13. 数据分析(Data Analysis)
  14. 阿里云商标注册购物车功能怎么用?在哪查看?
  15. 读侯世达之《哥德尔、艾舍尔、巴赫:集异壁之大成》
  16. 知识分享:数据分析的6大基本步骤
  17. Vue弹性标题栏(收缩扩张标题栏背景)
  18. android11 rom,第一个Android 11的第三方ROM来了,适用于红米K20 Pro
  19. win7无法自动检测修复计算机,无法启动,启动修复正在检查系统问题
  20. JAVA设计模式--建造者模式

热门文章

  1. 基于安卓的猫眼APP
  2. 长文对话实录:国内物联网10年沉浮,AIoT技术如何破局?| AIoT+智慧城市峰会
  3. CVPR 2018摘要:第五部分​
  4. nginx的事件处理worker_processes/worker_connections/worker_rlimit_nofile解释,查看cpu数量
  5. 基于Java毕业设计校园摄影爱好者交流网站源码+系统+mysql+lw文档+部署软件
  6. 关于 ubuntu18.04使用QtCcreator无法输入中文 的解决方法
  7. IDEA中使用Junit测试
  8. 云计算设计模式(十二)——索引表模式
  9. JavaScript面向对象和ES6笔记
  10. 面试题总结(持续更新中~)