OAuth 授权类型决定了 OAuth 过程中涉及的步骤的确切顺序。授权类型还会影响客户端应用程序在每个阶段与 OAuth 服务通信的方式,包括访问令牌本身的发送方式。因此,授权类型通常被称为“OAuth 流程”。
在客户端应用程序可以启动相应的流程之前,必须将 OAuth 服务配置为支持特定的授权类型。客户端应用程序在发送给 OAuth 服务的初始授权请求中指定要使用的授权类型。
有几种不同的授权类型,每种都有不同程度的复杂性和安全考虑。我们将重点关注“authentication code”和“(隐式) implicit”授权类型,因为它们是迄今为止最常见的
对于任何 OAuth 授权类型,客户端应用程序都必须指定它想要访问哪些数据以及它想要执行哪些操作。它使用授权请求的范围参数来实现这一点,发送到 OAuth 服务。
对于基本的OAuth,客户端应用程序可以请求访问的范围对于每个OAuth服务都是唯一的。由于作用域名称只是任意文本字符串,因此格式在提供者之间可能会有很大的差异。有些甚至使用完整的URI作为作用域名称,类似于REST API端点。例如,在请求读取用户联系人列表的访问权限时,作用域名称可能采用以下任何形式,具体取决于所使用的OAuth服务:
1 | scope=contacts |
当OAuth用于身份验证时,通常会使用标准化的OpenID Connect范围。例如,作用域openid profile将授予客户端应用程序读取用户的预定义基本信息的访问权限,例如他们的电子邮件地址、用户名等等。
授权码授权类型初始看起来很复杂,但一旦你熟悉了一些基础知识,它实际上比你想象的要简单。
简而言之,客户端应用程序和 OAuth 服务首先使用重定向来交换一系列基于浏览器的 HTTP 请求,以启动流程。用户被询问是否同意所请求的访问权限。如果他们同意,客户端应用程序将被授予“授权码”。然后,客户端应用程序将此代码与 OAuth 服务交换以接收“访问令牌”,它们可以使用该令牌进行 API 调用以获取相关用户数据。
所以说,要使用OAUTH的一个基础就是,服务器需要有可以用来提供数据的api咯?
从代码/令牌交换开始的所有通信都通过安全的、预配置的后端通道以服务器到服务器的方式发送,并且对终端用户不可见。当客户端应用程序首次向OAuth服务注册时,将建立此安全通道。此时还将生成client_secret,客户端应用程序必须使用它来在发送这些服务器到服务器请求时进行身份验证。
由于最敏感的数据(访问令牌和用户数据)不会通过浏览器发送,因此这种授权类型可以说是最安全的。服务器端应用程序应尽可能始终使用此授权类型。
流程图如下
客户端应用程序向OAuth服务的/authorization
端点发送请求,请求访问特定用户数据的权限。请注意,不同提供商之间的端点映射可能会有所不同
比如下面的请求
1 | GET /authorization?client_id=12345&redirect_uri=https://client-app.com/callback&response_type=code&scope=openid%20profile&state=ae13d489bd00e3c24 HTTP/1.1 |
他包含了一些比较关键的参数
client_id
1
2
3
4
5
必填参数,包含客户端应用程序的唯一标识符。当客户端应用程序在OAuth服务中注册时,将生成此值。
- ```
redirect_uri
当向客户端应用程序发送授权码时,用户浏览器应重定向到的 URI。这也被称为“回调 URI”或“回调终点”。**许多 OAuth 攻击**都基于利用此参数验证中的缺陷。
response_type
1
2
3
4
5
确定客户端应用程序期望的响应类型,因此确定它想要启动的流程。对于授权码授权类型,值应为 `code`。
- ```
scope
用于指定客户端应用程序想要访问的用户数据子集。请注意,这些可能是OAuth提供者设置的自定义范围或OpenID Connect规范定义的标准范围。
state
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
存储一个唯一的、不可猜测的值,该值与客户端应用程序上的当前会话相关联。OAuth 服务应在响应中返回此确切值,以及授权代码。此参数作为客户端应用程序的 CSRF 令牌,通过确保发往其 /callback 端点的请求来自启动 OAuth 流程的同一人来发挥作用。
#### 2. User login and consent
当授权服务器收到初始请求时,它会将用户重定向到登录页面,在那里他们将被提示使用OAuth提供者的帐户登录。例如,这通常是他们的社交媒体账户。比如qq,微信那些
然后用户将会看到一个数据列表,这是客户端应用程序想要访问的数据,基于授权请求中定义的范围。用户可以选择是否同意此访问。
需要注意的是,一旦用户为客户端应用程序批准了给定的范围,只要用户仍然与OAuth服务保持有效会话,此步骤将自动完成。换句话说,第一次用户选择“使用社交媒体登录”时,他们需要手动登录并给予同意,但是如果他们以后再次访问客户端应用程序,他们通常可以通过单击一次登录。
#### 3. Authorization code grant
如果用户同意请求的访问权限,他们的浏览器将被重定向到授权请求中`redirect_uri`指定的`/callback`端点。结果生成的GET请求将包含授权代码作为查询参数。根据配置,它还可以发送与授权请求中相同值的`state`参数。
```bash
GET /callback?code=a1b2c3d4e5f6g7h8&state=ae13d489bd00e3c24 HTTP/1.1
Host: client-app.com
一旦客户端应用程序收到授权码,它需要将其交换为访问令牌。为此,它向OAuth服务的/token端点发送服务器到服务器的POST请求。从这一点开始,所有通信都在安全的后台通道中进行,因此通常无法被攻击者观察或控制。
1 | POST /token HTTP/1.1 |
除了client_id和授权码之外,您还会注意到以下新参数:
client_secret
1
2
3
4
5
客户端应用程序必须通过包含注册 OAuth 服务时分配的密钥来验证自身身份。
- ```
grant_type
用于确保新端点知道客户端应用程序想要使用哪种授权类型。在这种情况下,应将其设置为`authorization_code`。
OAuth 服务将验证访问令牌请求。如果一切都符合预期,服务器将通过授予客户端应用程序所请求范围的访问令牌来响应。
1 | { |
现在客户端应用有了访问代码,他终于可以从资源服务器调取用户的数据了。为了实现这一点,它会向OAuth服务的/userinfo端点发出API调用。
访问令牌被提交到 Authorization: Bearer 标头中,以证明客户端应用程序有权限访问这些数据。
1 | GET /userinfo HTTP/1.1 |
资源服务器应该验证令牌是否有效,并且它属于当前的客户端应用程序
如果是这样,它将通过发送客户端请求的资源,即基于访问令牌的用户数据,来做出响应。
1 | { |
客户端应用程序最终可以将这些数据用于其预期的目的。在OAuth身份验证的情况下,它通常将被用作ID来授予用户已认证的会话,从而有效地登录他们。
隐式授权类型非常简单。与先获取授权码,再将其交换为访问令牌不同,Implicit授权类型中,客户端应用程序在用户同意后立即收到访问令牌(access token)。
你可能想知道为什么客户端应用程序不总是使用隐式授权类型。答案相对简单 - 它远不够安全。
当使用隐式授权类型时,所有通信都通过浏览器重定向进行 - 没有像授权代码流中那样的安全后通道。这意味着敏感访问令牌和用户数据更容易受到潜在攻击的影响。
隐式授权类型更适用于单页应用程序和本地桌面应用程序,这些应用程序不能轻松地将client_secret存储在后端,因此不像使用授权码授权类型那样受益。
图示
隐式流程的开始方式与授权码流程非常相似,只不过response_type参数必须被设置为token
1 | GET /authorization?client_id=12345&redirect_uri=https://client-app.com/callback&response_type=token&scope=openid%20profile&state=ae13d489bd00e3c24 HTTP/1.1 |
用户登录并决定是否同意所请求的权限。这个过程与授权码流程完全相同。
如果用户同意请求的访问权限,那么事情就开始有所不同了。OAuth 服务将重定向用户的浏览器到授权请求中指定的 redirect_uri
。然而,它不会发送包含授权码的查询参数,而是将访问令牌和其他令牌特定数据作为 URL 片段发送。
1 | GET /callback#access_token=z0y9x8w7v6u5&token_type=Bearer&expires_in=5000&scope=openid%20profile&state=ae13d489bd00e3c24 HTTP/1.1 |
由于访问令牌是作为 URL 片段发送的,因此它永远不会直接发送给客户端应用程序。相反,客户端应用程序必须使用适当的脚本来提取片段并存储它。
一旦客户端应用程序成功从URL片段中提取了访问令牌,它就可以使用它来调用OAuth服务的/userinfo
端点进行API调用。与授权码流程不同的是,这也是通过浏览器发生的。
1 | GET /userinfo HTTP/1.1 |
资源服务器应验证令牌是否有效,并且是否属于当前客户端应用程序。如果是,则会响应并发送所请求的资源,即基于访问令牌关联的范围的用户数据。
1 | { |
客户端应用程序最终可以将这些数据用于其预期的目的。在OAuth身份验证的情况下,它通常被用作一个ID来授予用户一个经过身份验证的会话,有效地将其登录。