什么是 OAuth?
从高层次开始,OAuth 不是API或服务:它是授权的开放标准,任何人都可以实施它。
更具体地说,OAuth 是应用程序可以用来为客户端应用程序提供“安全委托访问”的标准。OAuth 通过 HTTPS 工作,并使用访问令牌而不是凭据对设备、API、服务器和应用程序进行授权。
OAuth 有两个版本:OAuth 1.0a和OAuth 2.0。这些规范彼此完全不同,不能一起使用:它们之间没有向后兼容性。
哪一个更受欢迎?*好问题!如今,OAuth 2.0 是使用最广泛的 OAuth 形式。所以从现在开始,每当我说“OAuth”*时,我都是在谈论 OAuth 2.0——因为它很可能是您将要使用的。
为什么选择 OAuth?
OAuth 是作为对直接身份验证模式的响应而创建的。这种模式因 HTTP 基本身份验证而闻名,它会提示用户输入用户名和密码。基本身份验证仍然用作服务器端应用程序 API 身份验证的原始形式:用户发送 API 密钥 ID 和密码,而不是在每次请求时向服务器发送用户名和密码。在 OAuth 出现之前,网站会提示您直接在表单中输入用户名和密码,然后他们会以您的身份登录到您的数据(例如您的 Gmail 帐户)。这通常称为密码反模式.
为了为网络创建更好的系统,为单点登录 (SSO) 创建了联合身份。在这种情况下,最终用户与其身份提供者交谈,身份提供者生成一个加密签名的令牌,并将其交给应用程序以对用户进行身份验证。应用程序信任身份提供者。只要该信任关系适用于已签名的断言,您就可以开始了。下图显示了这是如何工作的。
联合身份因 SAML 2.0 而闻名,它是 2005 年 3 月 15 日发布的 OASIS 标准。这是一个很大的规范,但主要的两个组件是它的身份验证请求协议(也称为 Web SSO)和它打包身份属性并对其进行签名的方式,称为SAML 断言。
SAML
SAML 基本上是您浏览器中的一个会话 cookie,可让您访问网络应用程序。它在您可能希望在 Web 浏览器之外执行的设备配置文件类型和场景方面受到限制。
当 SAML 2.0 于 2005 年推出时,它是有道理的。然而,从那以后发生了很多变化。现在我们拥有现代网络和本机应用程序开发平台。有单页应用程序 (SPA),例如 Gmail/Google Inbox、Facebook 和 Twitter。它们的行为与您的传统 Web 应用程序不同,因为它们对 API 进行 AJAX(后台 HTTP 调用)。手机也进行 API 调用,电视、游戏机和物联网设备也是如此。SAML SSO 在这方面并不是特别擅长。
OAuth 和 API
我们构建 API 的方式也发生了很大变化。2005 年,人们投资于 WS-* 以构建 Web 服务。现在,大多数开发人员已转向 REST 和无状态 API。简而言之,REST 是通过网络推送 JSON 数据包的 HTTP 命令。
开发人员构建了很多 API。API 经济是您今天可能在董事会中听到的一个常见流行语。公司需要以允许许多设备访问它们的方式保护它们的 REST API。在过去,你会输入你的用户名/密码目录,应用程序会直接以你的身份登录。这就产生了委托授权问题。
“我怎样才能允许一个应用程序访问我的数据而不必给它我的密码?”
如果您曾经看过下面的对话框之一,那就是我们正在谈论的内容。这是一个询问是否可以代表您访问数据的应用程序。
这是 OAuth。
OAuth 是 REST/API 的委托授权框架。它使应用程序能够在不泄露用户密码的情况下获得对用户数据的有限访问(范围)。它将身份验证与授权分离,并支持解决不同设备功能的多个用例。它支持服务器到服务器应用程序、基于浏览器的应用程序、移动/本机应用程序和控制台/电视。
您可以将其视为酒店钥匙卡,但用于应用程序。如果您有酒店钥匙卡,则可以进入您的房间。您如何获得酒店钥匙卡?您必须在前台进行身份验证才能获得它。认证并获得钥匙卡后,您可以访问整个酒店的资源。
简单来说,OAuth 是:
- 应用请求用户授权
- 用户授权App并提交证明
- 应用程序向服务器提供授权证明以获取令牌
- 令牌仅限于访问用户为特定应用程序授权的内容
OAuth 中心组件
OAuth 建立在以下核心组件之上:
- Scopes and Consent (授权范围和用用户允许)
- Actors (用户)
- Clients (客户端)
- Tokens 认证票据
- Authorization Server (授权服务)
- Flows (授权过程)
OAuth Scope (OAuth 范围)
范围是您在应用程序请求权限时在授权屏幕上看到的内容。它们是客户端在请求令牌时要求的权限包。这些由应用程序开发人员在编写应用程序时编码。
范围将授权策略决策与执行分离。这是 OAuth 的第一个关键方面。权限是最重要的。它们并没有隐藏在您必须进行逆向工程的应用程序层后面。它们通常列在 API 文档中:以下是此应用程序需要的范围。
OAuth 是一种互联网规模的解决方案,因为它针对每个应用程序。您通常能够登录到仪表板以查看您已授予访问权限的应用程序并撤销同意。
OAuth 参与者
OAuth 流程中的参与者如下:
- 资源所有者:拥有资源服务器中的数据。例如,我是我的 Facebook 个人资料的资源所有者。
- 资源服务器:存储应用程序想要访问的数据的 API
- 客户端:想要访问您的数据的应用程序
- Authorization Server : OAuth的主要引擎
资源所有者是一个可以随着不同凭证而改变的角色。它可以是最终用户,也可以是公司。
客户可以是公开的和保密的。两者在 OAuth 命名法上有显着区别。可以信任机密客户端来存储秘密。它们不在桌面上运行或通过应用程序商店分发。人们无法对它们进行逆向工程并获得密钥。它们在最终用户无法访问的受保护区域中运行。
公共客户端是浏览器、移动应用程序和物联网设备。
客户端注册也是 OAuth 的一个关键组成部分。这就像 OAuth 的 DMV。您需要为您的申请获得牌照。这就是您的应用程序徽标在授权对话框中的显示方式。
OAuth 令牌
访问令牌是客户端用来访问资源服务器 (API) 的令牌。他们注定是短暂的。以小时和分钟来考虑它们,而不是几天和一个月。您不需要机密客户端来获取访问令牌。您可以通过公共客户端获取访问令牌。它们旨在针对互联网规模问题进行优化。因为这些令牌的寿命很短并且可以横向扩展,所以它们无法撤销,您只需等待它们超时即可。
另一个令牌是刷新令牌。这要长得多;天,月,年。这可用于获取新令牌。要获得刷新令牌,应用程序通常需要经过身份验证的机密客户端。
刷新令牌可以被撤销。在仪表板中撤销应用程序的访问权限时,您正在终止其刷新令牌。这使您能够强制客户端轮换机密。您正在做的是使用刷新令牌获取新的访问令牌,并且访问令牌通过网络访问所有 API 资源。每次刷新访问令牌时,您都会获得一个新的加密签名令牌。密钥轮换内置于系统中。
OAuth 规范没有定义令牌是什么。它可以是您想要的任何格式。不过通常情况下,您希望这些令牌是 JSON Web 令牌(标准)。简而言之,JWT(发音为“jot”)是一种安全可靠的令牌认证标准。JWT 允许您使用签名对信息(称为声明)进行数字签名,并可以在以后使用秘密签名密钥进行验证。要了解有关 JWT 的更多信息,请参阅A Beginner’s Guide to JWTs in Java。
令牌是从授权服务器上的端点检索的。两个主要端点是授权端点和令牌端点。它们针对不同的用例分开。授权端点是您从用户那里获得同意和授权的地方。这将返回一个授权授予,表明用户已同意它。然后将授权传递给令牌端点。令牌端点处理授权并说“很好,这是您的刷新令牌和访问令牌”。
您可以使用访问令牌来访问 API。一旦它过期,您将必须使用刷新令牌返回到令牌端点以获取新的访问令牌。
缺点是这会引起很多开发人员的摩擦。OAuth 对开发人员来说最大的痛点之一是您必须管理刷新令牌。您将状态管理推给每个客户端开发人员。您获得了密钥轮换的好处,但您刚刚给开发人员带来了很多痛苦。这就是开发人员喜欢 API 密钥的原因。他们只需复制/粘贴它们,将它们放入文本文件中,然后就可以完成了。API 密钥对开发人员来说非常方便,但对安全性很不利。
这里有一个付费游戏问题。让开发人员执行 OAuth 流程可以提高安全性,但也会有更多的摩擦。工具包和平台有机会简化事情并帮助进行代币管理。幸运的是,OAuth 如今已经相当成熟,而且您最喜欢的语言或框架很可能有可用的工具来简化事情。
我们已经讨论了一些有关客户端类型、令牌类型和授权服务器的端点以及我们如何将其传递给资源服务器的内容。我提到了两种不同的流程:获得授权和获得令牌。这些不必在同一频道上发生。前端通道是通过浏览器的。浏览器将用户重定向到授权服务器,用户同意。这发生在用户的浏览器上。一旦用户获得授权并将其交给应用程序,客户端应用程序就不再需要使用浏览器来完成 OAuth 流程来获取令牌。
令牌旨在由客户端应用程序使用,以便它可以代表您访问资源。我们称之为后台通道。反向通道是直接从客户端应用程序到资源服务器的 HTTP 调用,用于交换令牌的授权许可。这些通道用于不同的流,具体取决于您拥有的设备功能。
例如,您通过用户代理授权的前端通道流可能如下所示:
- 资源所有者开始流程以委托对受保护资源的访问
- 客户端通过浏览器重定向向授权服务器上的授权端点发送具有所需范围的授权请求
- 授权服务器返回一个同意对话框说“你允许这个应用程序访问这些范围吗?” 当然,您需要对应用程序进行身份验证,因此如果您未对资源服务器进行身份验证,它会要求您登录。如果您已经有一个缓存的会话 cookie,您只会看到同意对话框。查看同意对话框并同意。
- 授权授予通过浏览器重定向传递回应用程序。这一切都发生在前声道。
此流程中还有一个变体,称为隐式流程。我们会在一分钟内解决这个问题。
get https://accounts.google.com/o/oauth2/auth?scope=gmail.insert gmail.send
&redirect_uri=https://app.example.com/oauth2/callback
&response_type=code&client_id=812741506391
&state=af0ifjsldkj
这是一个带有一堆查询参数的 GET 请求(出于示例目的未进行 URL 编码)。范围来自 Gmail 的 API。redirect_uri 是授权授予应返回到的客户端应用程序的 URL。这应该与来自客户注册过程(在 DMV 处)的值相匹配。您不希望授权被退回到外国应用程序。响应类型因 OAuth 流而异。客户端 ID 也来自注册过程。State 是一个安全标志,类似于 XRSF。要了解有关 XRSF 的更多信息
HTTP/1.1 302 Found
Location: https://app.example.com/oauth2/callback?
code=MsCeLvIaQm6bTrgtp7&state=af0ifjsldkj
返回code
的是授权授予,state
是为了确保它不是伪造的,并且来自同一个请求。
Front Channel 完成后,会发生 Back Channel Flow,将授权代码交换为访问令牌。
客户端应用程序使用机密客户端凭据和客户端 ID 向授权服务器上的令牌端点发送访问令牌请求。此过程将授权代码授予交换访问令牌和(可选)刷新令牌。客户端使用访问令牌访问受保护的资源。
下面是它在原始 HTTP 中的样子:
Request
POST /oauth2/v3/token HTTP/1.1
Host: www.googleapis.com
Content-Type: application/x-www-form-urlencoded
code=MsCeLvIaQm6bTrgtp7&client_id=812741506391&client_secret={client_secret}&redirect_uri=https://app.example.com/oauth2/callback&grant_type=authorization_code
The grant_type is the extensibility part of OAuth. It’s an authorization code from a precomputed perspective. It opens up the flexibility to have different ways to describe these grants. This is the most common type of OAuth flow.
Response
{
"access_token": "2YotnFZFEjr1zCsicMWpAA",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "tGzv3JOkF0XG5Qx2TlKWIA"
}
响应是 JSON。您可以被动或主动使用令牌。主动是在你的客户中有一个计时器。反应式是捕获错误并尝试获取新令牌。
获得访问令牌后,您可以在身份验证标头中使用访问令牌(使用作为token_type
前缀)来发出受保护的资源请求。
curl -H "Authorization: Bearer 2YotnFZFEjr1zCsicMWpAA" \
https://www.googleapis.com/gmail/v1/users/1444587525/messages
所以现在您有一个前台通道、一个后台通道、不同的端点和不同的客户端。您必须针对不同的用例混合和匹配这些。这提高了 OAuth 的复杂性,并且会让人感到困惑。
OAuth 流程
第一个流就是我们所说的隐式流。之所以称为隐式流,是因为所有通信都是通过浏览器进行的。没有后端服务器为访问令牌兑换授权许可。SPA 是此流程用例的一个很好的示例。此流程也称为 2 Legged OAuth。
隐式流针对仅限浏览器的公共客户端进行了优化。访问令牌直接从授权请求返回(仅限前端通道)。它通常不支持刷新令牌。它假定资源所有者和公共客户端在同一台设备上。由于一切都发生在浏览器上,因此它最容易受到安全威胁。
黄金标准是 Authorization Code Flow,它同时使用前通道和后通道。这是我们在本文中讨论最多的内容。客户端应用程序使用前端通道流来获取授权码授予。客户端应用程序使用反向通道将授权代码授予交换访问令牌(以及可选的刷新令牌)。它假定资源所有者和客户端应用程序位于不同的设备上。这是最安全的流程,因为您可以对客户端进行身份验证以兑换授权授予,并且令牌永远不会通过用户代理传递。不仅有隐式和授权代码流程,您还可以使用 OAuth 执行其他流程。同样,OAuth 更像是一个框架。
对于服务器到服务器的场景,您可能希望使用Client Credential Flow。在这种情况下,客户端应用程序是一个机密客户端,它独立运行,不代表用户。它更像是一种服务帐户类型的场景。您只需要客户的凭据即可完成整个流程。这是一个反向通道,仅用于使用客户端的凭据获取访问令牌。它支持共享秘密或断言作为使用对称或非对称密钥签名的客户端凭证。
对称密钥算法是一种加密算法,只要您有密码,就可以解密任何内容。在保护 PDF 或 .zip 文件时经常会发现这种情况。
公钥密码术或非对称密码术是使用成对密钥的任何密码系统:公钥和私钥。公钥任何人都可以读取,私钥对所有者来说是神圣的。这允许数据安全而无需共享密码。
还有一种称为Resource Owner Password Flow 的遗留模式。这与使用用户名和密码的直接身份验证方案非常相似,因此不推荐使用。它是本地用户名/密码应用程序(例如桌面应用程序)的传统授权类型。在此流程中,您向客户端应用程序发送用户名和密码,然后它从授权服务器返回访问令牌。它通常不支持刷新令牌,并且假定资源所有者和公共客户端在同一台设备上。当您有一个只想使用 OAuth 的 API,但您有老派的客户要处理时。
OAuth 最近添加的是Assertion Flow,它类似于客户端凭证流。添加此内容是为了打开联邦的想法。此流程允许授权服务器信任来自第三方(例如 SAML IdP)的授权授予。授权服务器信任身份提供者。该断言用于从令牌端点获取访问令牌。这对于投资 SAML 或 SAML 相关技术并允许他们与 OAuth 集成的公司来说非常有用。因为 SAML 断言是短暂的,所以此流程中没有刷新令牌,您必须在每次断言过期时继续检索访问令牌。
不在 OAuth 规范中,是Device Flow。没有网络浏览器,只有电视之类的控制器。用户代码是从授权请求返回的,必须通过访问带有浏览器的设备上的 URL 来兑换授权。客户端应用程序使用反向通道流来轮询访问令牌和可选的刷新令牌的授权批准。也很受 CLI 客户端的欢迎。
我们已经介绍了使用不同参与者和令牌类型的六种不同流程。它们是必要的,因为客户的能力,我们需要如何获得客户的同意,谁正在同意,这给 OAuth 增加了很多复杂性。
当人们问您是否支持 OAuth 时,您必须澄清他们的要求。他们是在问您是否支持所有六个流程,还是只支持主要流程?所有不同的流程之间都有很多可用的粒度。
安全与企业
OAuth 的应用范围很广。使用隐式流,有很多重定向和很多错误空间。有很多人试图在应用程序之间利用 OAuth,如果您不遵循推荐的 Web Security 101 指南,这很容易做到。例如:
- 始终将 CSRF 令牌与
state
参数一起使用以确保流完整性 - 始终将重定向 URI 列入白名单以确保正确的 URI 验证
- 使用客户端 ID 将同一客户端绑定到授权授予和令牌请求
- 对于机密客户,确保客户机密不被泄露。不要将客户端机密放入通过 App Store 分发的应用程序中!
一般来说,对 OAuth 最大的抱怨来自于安全人员。它与 Bearer 令牌有关,它们可以像会话 cookie 一样传递。您可以传递它,一切顺利,它不会以加密方式绑定到用户。使用 JWT 很有帮助,因为它们无法被篡改。但是,最终,JWT 只是一串字符,因此可以轻松复制它们并在标头中使用Authorization
。
OpenID Connect
为了解决伪身份验证问题,结合了 OAuth 2.0、Facebook Connect 和 SAML 2.0 的最佳部分来创建OpenID Connect。id_token
OpenID Connect (OIDC) 使用新的客户端签名和UserInfo
获取用户属性的端点扩展 OAuth 2.0。与 SAML 不同,OIDC 提供了一组标准的身份范围和声明。示例包括:profile
、email
、address
和phone
。
OIDC 的创建是为了通过使事物完全动态化来实现 Internet 可扩展性。不再需要像 SAML 那样下载元数据和联合。有用于动态联合的内置注册、发现和元数据。您可以输入您的电子邮件地址,然后它会动态发现您的 OIDC 提供商,动态下载元数据,动态知道它将使用什么证书,并允许 BYOI(自带身份)。它支持企业的高保证级别和关键 SAML 用例。
OIDC 因谷歌和微软而闻名,这两家公司都是早期采用者。
Request
GET https://accounts.google.com/o/oauth2/auth?
scope= openid email &
redirect_uri=https://app.example.com/oauth2/callback&
response_type=code&
client_id=812741506391&
state=af0ifjsldkj
Response
HTTP/1.1 302 Found
Location: https://app.example.com/oauth2/callback?
code=MsCeLvIaQm6bTrgtp7&state=af0ifjsldkj
令牌响应的授权授予包含一个 ID 令牌。
Request:
POST /oauth2/v3/token HTTP/1.1
Host: www.googleapis.com
Content-Type: application/x-www-form-urlencoded
code=MsCeLvIaQm6bTrgtp7&client_id=812741506391&
client_secret={client_secret}&
redirect_uri=https://app.example.com/oauth2/callback&
grant_type=authorization_code
Response:
{
"access_token": "2YotnFZFEjr1zCsicMWpAA",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "tGzv3JOkF0XG5Qx2TlKWIA",
"id_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjFlOWdkazcifQ..."
}
您可以看到它在 OAuth 之上很好地分层,以将 ID 令牌作为结构化令牌返回。ID 令牌是 JSON Web 令牌 (JWT)。JWT(又名“jot”)比基于 XML 的巨大 SAML 断言小得多,可以在不同设备之间高效传递。JWT 包含三个部分:标头、正文和签名。标头说明使用什么算法对其进行签名,声明在正文中,并在签名中签名。
Open ID Connect 流程涉及以下步骤:
- 发现 OIDC 元数据
- 执行 OAuth 流程以获取 ID 令牌和访问令牌
- 获取 JWT 签名密钥并可选择动态注册客户端应用程序
- 根据内置日期和签名在本地验证 JWT ID 令牌
- 根据需要使用访问令牌获取其他用户属性
OAuth 2.0 总结
OAuth 2.0 是一种用于委托访问 API 的授权框架。它涉及请求资源所有者授权/同意的范围的客户端。授权授予交换访问令牌和刷新令牌(取决于流程)。有多个流程可以解决不同的客户端和授权场景。JWT 可用于授权服务器和资源服务器之间的结构化令牌。
OAuth 具有非常大的安全表面积。确保使用安全工具包并验证所有输入!
OAuth 不是身份验证协议。OpenID Connect 为身份验证方案扩展了 OAuth 2.0,通常称为“带大括号的 SAML”。