目录

OAuth 2.0

开放授权(OAuth)是一个开放标准,允许用户让第三方应用访问该用户在某一网站上存储的私密的资源(如照片,视频,联系人列表),而无需将用户名和密码提供给第三方应用。

OAuth允许用户提供一个令牌,而不是用户名和密码来访问他们存放在特定服务提供者的数据。每一个令牌授权一个特定的网站(例如,视频编辑网站)在特定的时段(例如,接下来的2小时内)内访问特定的资源(例如仅仅是某一相册中的视频)。这样,OAuth让用户可以授权第三方网站访问他们存储在另外服务提供者的某些特定信息,而非所有内容。

OAuth是OpenID的一个补充,但是完全不同的服务。

OAuth 2.0是OAuth协议的下一版本,但不向下兼容OAuth 1.0。OAuth 2.0关注客户端开发者的简易性,同时为Web应用、桌面应用、手机和智能设备提供专门的认证流程。

OAuth 2.0 Framework

OAuth Access Tokens

OAuth 访问令牌是 OAuth 客户端用来向资源服务器发出请求的字符串。

访问令牌不必采用任何特定格式,实际上,各种 OAuth 服务器为其访问令牌选择了许多不同的格式。

访问令牌可以是“承载令牌”或“发送者约束”令牌。发件人约束令牌要求 OAuth 客户端以某种方式证明拥有私钥才能使用访问令牌,这样访问令牌本身就无法使用。

访问令牌有许多属性是 OAuth 安全模型的基础:

  • OAuth 客户端不得读取或解释访问令牌。OAuth 客户端不是令牌的目标受众。
  • 访问令牌不会将用户身份或有关用户的任何其他信息传达给 OAuth 客户端。
  • 访问令牌只能用于向资源服务器发出请求。此外,不得使用ID 令牌向资源服务器发出请求。

OAuth Refresh Tokens

OAuth 刷新令牌是一个字符串,OAuth 客户端可以使用它来获取新的访问令牌,而无需用户交互。

刷新令牌不得允许客户端获得超出原始授权范围的任何访问权限。存在刷新令牌是为了使授权服务器能够对访问令牌使用较短的生命周期,而无需在令牌过期时让用户参与其中。

1
2
3
4
5
6
7
POST /oauth/token HTTP/1.1
Host: authorization-server.com
 
grant_type=refresh_token
&refresh_token=xxxxxxxxxxx
&client_id=xxxxxxxxxx
&client_secret=xxxxxxxxxx

OAuth Scopes

范围是 OAuth 2.0 中的一种机制,用于限制应用程序对用户帐户的访问。应用程序可以请求一个或多个范围,然后在同意屏幕中将此信息呈现给用户,并且颁发给应用程序的访问令牌将仅限于授予的范围。

OAuth 规范允许授权服务器或用户修改授予应用程序的范围(与请求的范围相比),尽管在实践中这样做的服务示例并不多。

OAuth 没有为范围定义任何特定值,因为它高度依赖于服务的内部架构和需求。

OAuth Grant Types

Authorization Code 授权码

授权代码授权类型由机密和公共客户端用于交换访问令牌的授权代码。

用户通过重定向 URL 返回客户端后,应用程序会从 URL 中获取授权码,并使用它来请求访问令牌。

建议所有客户端也将此流程与PKCE扩展一起使用,以提供更好的安全性。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
     +----------+
     | 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)

   Note: The lines illustrating steps (A), (B), and (C) are broken into
   two parts as they pass through the user-agent.

                     Figure 3: Authorization Code Flow

(A)用户访问客户端,后者将前者导向认证服务器。

(B)用户选择是否给予客户端授权。

(C)假设用户给予授权,认证服务器将用户导向客户端事先指定的"重定向URI"(redirection URI),同时附上一个授权码。

(D)客户端收到授权码,附上早先的"重定向URI",向认证服务器申请令牌。这一步是在客户端的后台的服务器上完成的,对用户不可见。

(E)认证服务器核对了授权码和重定向URI,确认无误后,向客户端发送访问令牌(access token)和更新令牌(refresh token)。

1
2
3
4
5
6
7
8
9
POST /oauth/token HTTP/1.1
Host: authorization-server.com
 
grant_type=authorization_code
&code=xxxxxxxxxxx
&redirect_uri=https://example-app.com/redirect
&code_verifier=Th7UHJdLswIYQxwSg29DbK1a_d9o41uNMTRmuH0PM8zyoMAQ
&client_id=xxxxxxxxxx
&client_secret=xxxxxxxxxx

PKCE Proof Key for Code Exchange

PKCE ( RFC 7636 ) 是授权代码流程的扩展,用于防止 CSRF 和授权代码注入攻击。

PKCE不能替代客户端密钥,即使客户端正在使用客户端密钥,也建议使用 PKCE。

注意:因为 PKCE 不能替代客户端身份验证,所以它不允许将公共客户端视为机密客户端。

PKCE 最初旨在保护移动应用程序中的授权代码流,但其防止授权代码注入的能力使其适用于每种类型的 OAuth 客户端,甚至是使用客户端密码的 Web 应用程序。

1
2
3
4
5
6
7
8
9
POST /oauth/token HTTP/1.1
Host: authorization-server.com
 
response_type=code
&client_id=xxxxxxxxxxx
&redirect_uri=https://example-app.com/redirect
&state=1234zyx
&code_challenge=XXXXXXXXX
&code_challenge_method=S256

Client Credentials 客户端

客户端使用客户端凭据授予类型来获取用户上下文之外的访问令牌。

这通常被客户端用来访问关于他们自己的资源,而不是访问用户的资源。

1
2
3
4
5
6
7
8
9
     +---------+                                  +---------------+
     |         |                                  |               |
     |         |>--(A)- Client Authentication --->| Authorization |
     | Client  |                                  |     Server    |
     |         |<--(B)---- Access Token ---------<|               |
     |         |                                  |               |
     +---------+                                  +---------------+

                     Figure 6: Client Credentials Flow

(A)客户端向认证服务器进行身份认证,并要求一个访问令牌。 (B)认证服务器确认无误后,向客户端提供访问令牌。

1
2
3
4
5
6
POST /token HTTP/1.1
Host: authorization-server.com
 
grant_type=client_credentials
&client_id=xxxxxxxxxx
&client_secret=xxxxxxxxxx

Device Code

设备代码授权类型由设备流中的无浏览器或输入受限设备使用,以将先前获得的设备代码交换为访问令牌。

设备代码授权类型值为 urn:ietf:params:oauth:grant-type:device_code

1
2
3
4
5
6
7
POST /token HTTP/1.1
Host: authorization-server.com
Content-type: application/x-www-form-urlencoded
 
grant_type=urn:ietf:params:oauth:grant-type:device_code&
client_id=a17c21ed&
device_code=NGU5OWFiNjQ5YmQwNGY3YTdmZTEyNzQ3YzQ1YSA

Refresh Token

当访问令牌过期时,客户端使用刷新令牌授权类型将刷新令牌交换为访问令牌。

这允许客户端继续拥有有效的访问令牌,而无需与用户进行进一步交互。

1
2
3
4
5
6
7
POST /oauth/token HTTP/1.1
Host: authorization-server.com
 
grant_type=refresh_token
&refresh_token=xxxxxxxxxxx
&client_id=xxxxxxxxxx
&client_secret=xxxxxxxxxx

Legacy: Implicit Flow 隐式授权

隐式流程是以前推荐用于本机应用程序和 JavaScript 应用程序的简化 OAuth 流程,其中访问令牌立即返回,无需额外的授权代码交换步骤。

不建议使用隐式流(并且某些服务器完全禁止此流),因为在 HTTP 重定向中返回访问令牌而没有任何确认客户端已收到它的固有风险。

本地应用程序和 JavaScript 应用程序等公共客户端现在应该使用带有PKCE扩展的授权代码流。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
     +----------+
     | 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 |
     |         |
     +---------+

   Note: The lines illustrating steps (A) and (B) are broken into two
   parts as they pass through the user-agent.

                       Figure 4: Implicit Grant Flow

(A)客户端将用户导向认证服务器。

(B)用户决定是否给于客户端授权。

(C)假设用户给予授权,认证服务器将用户导向客户端指定的"重定向URI",并在URI的Hash部分包含了访问令牌。

(D)浏览器向资源服务器发出请求,其中不包括上一步收到的Hash值。

(E)资源服务器返回一个网页,其中包含的代码可以获取Hash值中的令牌。

(F)浏览器执行上一步获得的脚本,提取出令牌。

(G)浏览器将令牌发给客户端。

Authorization Request

1
2
3
4
5
6
POST /oauth/token HTTP/1.1
Host: authorization-server.com
 
response_type=token
&client_id=xxxxxxxxxx
&state=xxxxxxxxxx

Legacy: Password Grant 密码

密码授权类型是一种将用户凭据交换为访问令牌的方法。因为客户端应用程序必须收集用户的密码并将其发送到授权服务器,所以不建议再使用此授权。

此流程没有为多因素身份验证或委托帐户等机制提供任何机制,因此在实践中非常有限。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
     +----------+
     | Resource |
     |  Owner   |
     |          |
     +----------+
          v
          |    Resource Owner
         (A) Password Credentials
          |
          v
     +---------+                                  +---------------+
     |         |>--(B)---- Resource Owner ------->|               |
     |         |         Password Credentials     | Authorization |
     | Client  |                                  |     Server    |
     |         |<--(C)---- Access Token ---------<|               |
     |         |    (w/ Optional Refresh Token)   |               |
     +---------+                                  +---------------+

            Figure 5: Resource Owner Password Credentials Flow

(A)用户向客户端提供用户名和密码。 (B)客户端将用户名和密码发给认证服务器,向后者请求令牌。 (C)认证服务器确认无误后,向客户端提供访问令牌。

1
2
3
4
5
6
7
8
POST /oauth/token HTTP/1.1
Host: authorization-server.com
 
grant_type=password
&username=user@example.com
&password=1234luggage
&client_id=xxxxxxxxxx
&client_secret=xxxxxxxxxx

Access Token Response

成功的响应

  • access_token(必需)授权服务器颁发的访问令牌字符串。
  • token_type(必需)这是令牌的类型,通常只是字符串“Bearer”。
  • expires_in(推荐)如果访问令牌过期,服务器应回复授予访问令牌的持续时间。
  • refresh_token(可选)如果访问令牌将过期,则返回一个刷新令牌很有用,应用程序可以使用该刷新令牌获取另一个访问令牌。但是,使用隐式授权颁发的令牌不能颁发刷新令牌。
  • scope(可选)如果用户授予的范围与应用请求的范围相同,则此参数是可选的。如果授予的范围与请求的范围不同,例如用户修改了范围,则需要此参数。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store
 
{
  "access_token":"MTQ0NjJkZmQ5OTM2NDE1ZTZjNGZmZjI3",
  "token_type":"Bearer",
  "expires_in":3600,
  "refresh_token":"IwOGYzYTlmM2YxOTQ5MGE3YmNmMDFkNTVk",
  "scope":"create"
}

OAuth 2.0 客户端类型

OAuth 定义了两种类型的客户端:机密客户端和公共客户端。

机密客户端是能够安全地通过授权服务器进行身份验证的应用程序,例如能够保持其注册客户端机密的安全。

公共客户端无法使用已注册的客户端机密,例如在浏览器或移动设备上运行的应用程序。

OAuth 2.0 Bearer Token Usage

不记名令牌(Bearer Token)是用于 OAuth 2.0 的主要访问令牌类型。

Bearer Token 是一个不透明的字符串,对使用它的客户端没有任何意义。一些服务器会发出一串十六进制字符的令牌,而其他服务器可能会使用结构化令牌,例如JSON Web Tokens。

  • Token Introspection(自省) (RFC 7662)
  • Token Revocation(撤销) (RFC 7009)
  • JSON Web Token (RFC 7519)
  • JWT Profile for Access Tokens

名词定义

角色 Roles

OAuth 定义了四个角色

  • Resource owner(资源所有者) (用户)
  • Resource server(资源服务器) (the API)
  • Authorization server(授权服务器) (可以和 API 同一个服务器)
  • Client(客户端) (应用程序)

User Agent:用户代理,一般指浏览器。

用户 The User

OAuth 2.0 规范将用户称为“资源所有者”。资源所有者是授予对其帐户某些部分的访问权限的人。在这种情况下,资源可以是数据(照片、文档、联系人)、服务(发布博客条目、转移资金)或任何其他需要访问限制的资源。任何想要代表用户行事的系统都必须首先获得他们的许可。

The API

该规范将您通常认为的主要 API 称为“资源服务器”。资源服务器是包含第三方应用程序正在访问的用户信息的服务器。如果用户允许,资源服务器必须能够接受和验证访问令牌并授予请求。资源服务器不一定需要了解应用程序。

授权服务器 The Authorization Server

授权服务器是用户在应用程序请求访问其帐户时与之交互的对象。这是显示 OAuth 提示的服务器,也是用户批准或拒绝应用程序请求的地方。授权服务器还负责在用户授权应用程序后授予访问令牌。

客户端 The Client

客户端是尝试代表用户执行操作或访问用户资源的应用程序。在客户端可以访问用户的帐户之前,它需要获得权限。客户端将通过将用户定向到授权服务器来获得许可,或者通过直接向授权服务器声明许可而无需用户交互。

机密客户 Confidential Clients

机密客户是有能力维护client_secret. 通常,这些客户端只是在开发人员控制下的服务器上运行的应用程序,用户无法访问源代码。这些类型的应用程序通常被称为“Web 应用程序”,因为它们通常在 Web 服务器上运行。

公共客户 Public Clients

公共客户端无法维护 a 的机密性client_secret,因此这些应用程序不使用该机密。移动应用程序和 Javascript 应用程序都被视为公共客户端。由于运行 Javascript 应用程序的任何人都可以轻松查看应用程序的源代码,因此在那里可以轻松看到一个秘密。对于移动应用程序,可以对二进制文件进行反编译以提取字符串。每当应用程序在用户控制的设备上运行时,它都应该被视为公共客户端。

访问令牌 Access Token

访问令牌是向 API 发出经过身份验证的请求时使用的字符串。该字符串本身对使用它的应用程序没有任何意义,但表示用户已授权第三方应用程序访问其帐户。令牌具有相应的访问持续时间、范围以及服务器需要的其他潜在信息。

刷新令牌 Refresh Token

刷新令牌是一个字符串,用于在访问令牌过期时获取新的访问令牌。并非所有 API 都使用刷新令牌。

授权码 Authorization Code

授权代码是在服务器端应用程序流中使用的中间令牌,在服务器端应用程序中有更详细的描述。授权步骤后,授权码返回给客户端,然后客户端将其交换为访问令牌。

Protocol Flow 协议运行流程

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
 +--------+                               +---------------+
     |        |--(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 ---|               |
     +--------+                               +---------------+

                     Figure 1: Abstract Protocol Flow

(A)用户打开客户端以后,客户端要求用户给予授权。

(B)用户同意给予客户端授权。

(C)客户端使用上一步获得的授权,向认证服务器申请令牌。

(D)认证服务器对客户端进行认证以后,确认无误,同意发放令牌。

(E)客户端使用令牌,向资源服务器申请获取资源。

(F)资源服务器确认令牌无误,同意向客户端开放资源。

不难看出来,上面六个步骤之中,B是关键,即用户怎样才能给于客户端授权。有了这个授权以后,客户端就可以获取令牌,进而凭令牌获取资源。

附录