OAuth 授权实际实现这些模式,实际使用场景有5种模式:
- 授权码授予
- 隐式授权
- 资源所有者的密码凭证授予
- 客户凭证授予
- 设备授权
分别通过详细说明美中模式的使用场景和差异。
1、授权码授予
这是最常见的 OAuth 授权,也是最安全的。它依赖于用户与浏览器(Chrome、Firefox、Safari 等)的交互来处理。此授权需要用户交互,因此不适用于机器对机器授权模式。我们上面介绍的所有交互模式都涉及相同的参与方和 UI,除非显示“权限授予屏幕”。
在我们深入研究这项拨款之前,我们需要定义一些术语。
- 授权端点:这是启动工作流的位置,是浏览器转到的 URL。通常,用户在此位置注册或登录。
- 授权码:这是用户注册或登录后 OAuth 服务器在重定向中包含的可打印 ASCII 字符的随机字符串。这由应用程序后端交换为令牌。
- Token 端点:这是一个 API,用于在用户登录后从 OAuth 服务器获取令牌。应用程序后端在调用Token 端点时使用授权码。
PKCE(代码交换的证明密钥 - 发音为 Pixy)。PKCE 是一个位于授权码授权之上的安全层,以确保授权码不会被窃取或重复使用。应用程序生成一个密钥(称为代码验证器)并使用 SHA-256 对其进行哈希处理。此哈希是单向的,因此攻击者无法反转它。然后,应用程序将散列发送到 OAuth 服务器,由 OAuth 服务器存储它。稍后,当应用程序从 OAuth 服务器获取令牌时,应用程序将向服务器发送密钥,并且 OAuth 服务器将验证提供的密钥的哈希值是否与先前提供的值匹配。这是对可以拦截授权码但没有密钥的攻击者的良好保护。
注意:client_id
当应用程序后端将和传递client_secret
给令牌端点时,标准 Web 浏览器使用 OAuth 和授权码授权不需要 PKCE 。我们将在下面更详细地介绍这一点,但根据您的实施,您可能可以安全地跳过实施 PKCE。我建议始终使用它,但并不总是需要它。
让我们看看如何使用预构建的 OAuth 服务器(如 FusionAuth)来实现此授权。
一般通过到auth服务器的连接,配置固定格式的授权端点鉴权参数,通过链接触发,如:
<a href="https://login.twgtl.com/oauth2/authorize?[a bunch of parameters here]">Login</a>
授权端点参数说明
此代码立即将浏览器重定向到 OAuth 服务器。但是,如果您运行此代码并单击该链接,OAuth 服务器将拒绝该请求,因为它不包含所需的参数。OAuth 规范中定义的参数有:
client_id
- 这标识了您正在登录的应用程序。在 OAuth 中,这称为client
. 该值将由 OAuth 服务器提供给您。redirect_uri
- 这是您的应用程序中的 URL,用户登录后 OAuth 服务器会将用户重定向到该 URL。此 URL 必须在 OAuth 服务器上注册,并且必须指向您的应用程序中的控制器(而不是静态页面),因为在调用此 URL 后,您的应用程序必须执行额外的工作。state
- 从技术上讲,此参数是可选的,但它对于防止各种安全问题很有用。OAuth 服务器将此参数回显到您的应用程序。它可以是您可能需要在 OAuth 工作流程中保留的任何内容。如果您对该参数没有其他需求,我建议将其设置为较大的随机字符串。如果您需要在整个工作流程中保留数据,我建议设置 URL 编码数据并附加随机字符串。response_type
- 这应该始终设置code
为这个授权。这告诉 OAuth 服务器您正在使用授权码授权。scope
- 这也是一个可选参数,但在上述某些模式下,这将是 OAuth 服务器所必需的。此参数是一个空格分隔的字符串列表。offline
如果您计划在应用程序中使用刷新令牌,您可能还需要在此列表中包含范围(我们稍后会刷新令牌)。code_challenge
- 这是一个可选参数,但提供对 PKCE 的支持。当没有可以处理授权码授予的最后步骤的后端时,这很有用。这被称为“公共客户”。没有后端的应用程序的情况并不多,但是如果您有类似移动应用程序的东西并且您无法利用服务器端后端进行 OAuth,则必须实施 PKCE 以保护您的应用程序免受安全性问题。围绕 PKCE 的安全问题超出了本指南的范围,但您可以在网上找到大量关于它们的文章。OAuth 2.1 草案也推荐使用 PKCE 。code_challenge_method
- 这是一个可选参数,但如果您实现 PKCE,则必须指定您的 PKCEcode_challenge
参数是如何创建的。它可以是plain
或S256
。我们从不推荐使用任何东西,除非S256
它使用 SHA-256 安全散列来进行 PKCE。nonce
- 这是一个可选参数,用于 OpenID Connect。我们不会在本指南中详细介绍 OpenID Connect,但我们将介绍一些方面,包括 Id 令牌和nonce
参数。该nonce
参数将包含在 OAuth 服务器生成的 Id 令牌中。我们可以在检索 Id 令牌时验证这一点。这将在后面讨论。
重定向和检索令牌
用户登录后,OAuth 服务器将浏览器重定向回应用程序。重定向的确切位置由redirect_uri
我们在上面的 URL 中传递的参数控制。在我们的示例中,此位置是https://app.twgtl.com/oauth-callback
。当 OAuth 服务器将浏览器重定向回该位置时,它会向 URL 添加一些参数。这些都是:
code
- 这是用户登录后 OAuth 服务器创建的授权代码。我们将用此代码交换令牌。state
state
- 这与我们传递给 OAuth 服务器的参数值相同。这将回显给应用程序,以便应用程序可以验证code
来自正确的位置。
OAuth 服务器可以根据需要添加其他参数,但这些是规范中定义的唯一参数。完整的重定向 URL 可能如下所示:
<span style="color:#0f1314"><span style="background-color:#ffffff"><span style="background-color:#272822"><span style="color:#d4d4d4"><code>https://app.twgtl.com/oauth-callback?code=123456789&state=foobarbaz
</code></span></span></span></span>
请记住,浏览器将向该 URL 发出 HTTPGET
请求。为了安全地完成 OAuth 授权代码授权,您应该编写服务器端代码来处理此 URL 上的参数。这样做将允许您安全地交换code
令牌的授权参数。
通过code获取访问令牌
我们需要知道 OAuth 服务器的 Token 端点的位置。OAuth 服务器提供此端点,它将验证授权code
并将其交换为令牌。我们使用 FusionAuth 作为我们的示例 OAuth 服务器,它具有一致的 Token 端点位置。(其他 OAuth 服务器可能有不同或不同的位置;请查阅您的文档。)在我们的示例中,该位置将是https://login.twgtl.com/oauth2/token
.
POST
我们将需要使用多个参数的表单编码值向 Token 端点发出 HTTP请求。以下是我们需要发送到 Token 端点的参数:
code
- 这是我们交换令牌的授权码。client_id
- 这是标识我们的应用程序的客户端 ID。client_secret
- 这是 OAuth 服务器提供的密钥。这不应该公开,只应该存储在服务器上的应用程序中。code_verifier
- 这是我们在上面创建的代码验证器值,存储在会话或 cookie 中。grant_type
- 这将始终是authorization_code
让 OAuth 服务器知道我们正在向它发送授权码的值。redirect_uri
- 这是我们发送到上面的 OAuth 服务器的重定向 URI。它必须是完全相同的值。
已经成功地交换了code
令牌的授权,让我们看看我们从 OAuth 服务器收到的令牌。我们将假设 OAuth 服务器使用 JWT(JSON Web 令牌)作为访问和 Id 令牌。OAuth2 没有定义任何令牌格式,但实际上访问令牌通常是 JWT。另一方面,OpenId Connect (OIDC) 要求id_token
是 JWT。
以下是我们拥有的令牌:
access_token
:这是一个 JWT,其中包含有关用户的信息,包括他们的 id、权限以及我们可能需要从 OAuth 服务器获得的任何其他信息。id_token
:这是一个 JWT,其中包含有关用户的公共信息,例如他们的姓名。此令牌通常可以安全地存储在非安全 cookie 或本地存储中,因为它不能用于代表用户调用 API。refresh_token
:这是一个不透明的令牌(不是 JWT),可用于创建新的访问令牌。访问令牌过期并且可能需要更新,具体取决于您的要求(例如,您希望访问令牌持续多长时间与您希望用户保持登录状态多长时间)。
由于我们拥有的两个令牌是 JWT,让我们在这里快速介绍该技术。JWT 的全面介绍超出了本指南的范围,但在我们的令牌专家建议部分中有几个很好的 JWT 指南。
JWT 是 JSON 对象,包含有关用户的信息,也可以签名。JSON 对象的键称为“声明”。JWT 会过期,但在此之前它们可以呈现给 API 和其他资源以获取访问权限。保持它们的生命周期较短,并像保护其他凭证(如 API 密钥)一样保护它们。因为它们是经过签名的,所以可以验证 JWT 以确保它没有被篡改。JWT 有几个标准声明。这些主张是:
aud
:JWT 的目标受众。这通常是一个标识符,您的应用程序应验证此值是否符合预期。exp
: JWT 的过期时刻。这存储为自 Epoch(1970 年 1 月 1 日 UTC)以来的秒数。iss
:创建 JWT 的系统的标识符。这通常是在 OAuth 服务器中配置的值。您的应用程序应验证此声明是否正确。nbf
:JWT 生效的时刻。它代表“不是之前”。这存储为自 Epoch(1970 年 1 月 1 日 UTC)以来的秒数。sub
: 这个 JWT 的主题。通常,这是用户的 ID。
JWT 还有其他您应该注意的标准声明。您可以查看这些规范以获取其他标准声明列表:
2、OAuth 2.0 中的隐式授权
从 OAuth 2.1 草案规范的最新版本开始,隐式授权已从 OAuth 中删除。它已被删除的原因是它跳过了一个重要步骤,该步骤允许您保护从 OAuth 服务器收到的令牌。此步骤发生在您的应用程序后端调用 Token 端点以检索令牌时。
与授权码授权不同,隐式授权不会使用授权码将浏览器重定向到您的应用程序服务器。相反,它将访问令牌作为重定向的一部分直接放在 URL 上。这些 URL 如下所示:
https://my-app.com/#token-goes-here
令牌在符号之后添加到重定向 URL #
,这意味着它在技术上是 URL 的片段部分。这真正意味着,无论 OAuth 服务器将浏览器重定向到何处,基本上每个人都可以访问访问令牌。
具体来说,在浏览器中运行的任何和所有 JavaScript 都可以访问访问令牌。由于此令牌允许浏览器代表用户进行 API 调用和 Web 请求,因此让第三方代码可以访问此令牌是极其危险的。
3、资源所有者的密码凭证授予
密码授权允许应用程序通过本机表单直接从用户那里收集用户名和密码,并将此信息发送到 OAuth 服务器。OAuth 服务器验证此信息,然后返回访问令牌和可选的刷新令牌。
许多移动应用程序和旧版 Web 应用程序使用此授权,因为他们希望向用户呈现与其应用程序原生的登录 UI。在大多数情况下,移动应用程序不希望打开 Web 浏览器来登录用户,而 Web 应用程序希望将用户保留在其 UI 中,而不是将浏览器重定向到 OAuth 服务器。
这种方法有两个主要问题:
- 应用程序正在收集用户名和密码并将其发送到 OAuth 服务器。这意味着应用程序必须确保用户名和密码完全安全。这与仅将用户名和密码直接提供给 OAuth 服务器的授权代码授权不同。
- 此授权不支持您的 OAuth 服务器可能提供的任何辅助安全功能,例如:
- 多因素身份验证
- 密码重置
- 设备授权
- 登记
- 电子邮件和帐户验证
- 无密码登录
由于此授权的限制和不安全性,它已从 OAuth 规范的最新草案中删除。建议不要在生产中使用它。
4、客户凭证授予
Client Credentials 授予为一个client
人授权另一个人提供了能力client
。在 OAuth 术语中,aclient
是应用程序本身,独立于用户。因此,此授权最常用于允许一个应用程序调用另一个应用程序,通常是通过 API。因此,该授权实现了上述机器对机器授权模式。
使用 Client Credentials 授权,没有用户可以登录。
Client Credentials 授权利用 OAuth 服务器的 Token 端点,并发送几个参数作为表单数据以生成访问令牌。然后使用这些访问令牌调用 API。以下是此授权所需的参数:
client_id
- 这是标识源应用程序的客户端 ID。client_secret
- 这是 OAuth 服务器提供的密钥。这不应该公开,只应该存储在源应用程序服务器上。grant_type
- 这将始终是client_credentials
让 OAuth 服务器知道我们正在使用客户端凭据授权的值。
您可以在请求正文中发送client_id
和client_secret
,也可以使用标头中的基本访问授权发送它们Authorization
。我们会将它们发送到下面的正文中,以保持与上面授权代码授权中的代码一致。
让我们重新设计我们的 TWGTL 应用程序以使用 Client Credentials 授权,以支持两个不同的后端相互调用 API。如果您从上面回忆起,我们完成 TWGTL ToDo 项目的代码也发出了 WUPHF。这都是内联的,但可以分成不同的后端或微服务。我听说微服务现在很热门。
5、设备授权
机器对机器授权OAuth 模式与我们之前介绍的模式不同。这种模式根本不涉及用户。相反,它允许一个应用程序与另一个应用程序交互。通常,这是通过 API 相互通信的后端服务。
在这里,一个后端需要被授予对另一个后端的访问权限。我们将第一个后端称为源,第二个后端称为目标。为此,源向 OAuth 服务器进行身份验证。OAuth 服务器确认源的身份,然后返回源将用于调用目标的令牌。此令牌还可以包括目标用于授权源正在进行的调用的权限。