作者 | Robin Guldener
策划 | 言征
OAuth 是一个标准协议。基本上你可以想象的每种编程语言都有 OAuth 2.0 的客户端库。
但有了客户端库,却并不意味着万事大吉,如果你能够在大约 10 分钟内做到为任何 API 实施 OAuth。或者至少在一个小时内,请给我们发电子邮件——我们想请你吃一顿美味的晚餐,并听听你是怎么做到的。
我们为 50 个最流行的 API 实现了 OAuth,例如 google(GmAIl、Calendar、Sheets 等)、HubSpot、Shopify、Salesforce、Stripe、Jira、Slack、Microsoft(Azure、Outlook、OneDrive)、LinkedIn、Facebook 和其他 OAuth API等。
我们的结论:现实世界的 OAuth 体验可与 2008 年的 JAVAScript 浏览器 API 相媲美。人们普遍认为应该如何做事,但实际上每个 API 都有自己对标准、实现怪癖以及非标准行为和扩展的解释. 结果:到处都是坑。
“这个 API 也使用 OAuth 2.0,我们几周前就已经这样做了。我应该在明天之前完成。”
——实习生的著名遗言
OAuth 是一个非常大的标准。OAuth 2.0 的官方网站目前列出了 17 个不同的 RFC(定义标准的文档),它们共同定义了 OAuth 2 的工作方式。它们涵盖了从 OAuth 框架和 Bearer 令牌到威胁模型和私钥 JWT 的所有内容。
“但是,”我听到你说,“肯定不是所有这些 RFC 都与使用 API 的简单第三方访问令牌授权相关吗?”
你说得对。让我们只关注可能与典型的 API 第三方访问用例相关的事情:
OAuth 标准:OAuth 2.0 现在是默认的,但是 OAuth 1.0a 仍然被一些人使用(2.1 即将到来)。一旦你知道你的 API 使用了哪一个,请继续。
授予类型:你需要authorization_code、client_credentials还是device_code?它们的作用是什么,你应该在什么时候使用它们?如有疑问,请尝试authorization_code。
旁注:刷新令牌也是一种授权类型,但有点特殊。它们的工作方式是标准化的,但你最初要求它们的方式却不是。稍后会详细介绍。
现在你已准备好处理你的请求,让我们看看许多(准确地说是 72 个)具有定义的含义和行为的官方 OAuth 参数。常见示例有prompt、scope、audience、resource、assertion和login_hint。然而,根据我们的经验,大多数 API 提供者似乎都没有注意到这个列表,就像你可能直到现在一样,所以不要太担心它。
如果你认为这仍然感觉太复杂并且需要学习很多东西,我们倾向于同意你的看法。
大多数构建公共 API 的团队似乎也同意这一点。他们没有实现完整的 OAuth 2.0 子集,而是只实现了他们认为 API 用例所需的 OAuth 部分。这导致文档中有相当长的页面概述了 OAuth 如何为这个特定的 API 工作。但是我们很难责怪他们;他们的 DX 只考虑最好的意图。如果他们真的试图实施完整的标准,你需要阅读一本小书!
Salesforce authorization_code OAuth 流程。为什么不喜欢这个简单的 10 步过程的清晰视觉效果?
问题在于每个人对于 OAuth 的哪个子集与他们相关的想法略有不同,因此你最终会得到许多不同的(子)实现。
由于每个 API 都实现了不同的 OAuth 子集,你很快就会陷入被迫详细阅读 OAuth 文档的长页的情况:
他们在授权调用中需要哪些参数?
他们希望在令牌请求调用中看到什么?
我应该在哪里重定向我的用户进行授权?
但至少我可以选择我的回调 URL,不是吗?
如果你输入http://localhost:3003/callback作为 Slack API 的回调,他们会友善地提醒你“请使用 https 确保安全”。是的,也适用于本地主机。幸运的是,在 localhost 上有 OAuth 重定向的解决方案。
我们可以继续讨论很长时间,但我们认为你现在可能明白了。
尽管 OAuth 标准非常庞大,但许多 API 似乎仍然在其中寻找所需功能的差距。我们看到的一个常见问题是,除了access_token之外,你还需要一些数据才能使用 API。如果这些额外的数据可以与 OAuth 流中的 access_token 一起返回给你,那不是很好吗?
我们实际上认为这是一个好主意——或者至少比强迫用户在之后执行古怪的额外 API 请求来获取此信息(看看你,Jira)要好。但这确实意味着你特别需要为每个 API 实现更多非标准行为。
以下是我们见过的一小部分非标准扩展:
为了简洁起见,我们将跳过我们遇到的许多非真正标准的 OAuth 流程。
调试分布式系统总是很困难。当你使用的服务使用广泛的、通用的错误消息时,这会变得更加困难。
OAuth2 有标准化的错误消息,但它们在告诉你正在发生的事情方面与上面标题中的示例一样有用(顺便说一下,这是 OAuth 标准推荐的错误消息之一)。
你可能会争辩说 OAuth 是一个标准并且每个 API 都有文档,那么有什么可以调试的呢?
很多。我无法告诉你文档出错的频率。或者缺少一个细节。或者还没有更新最新的变化。或者当你第一次看到它们时你错过了什么。我们实施的 80% 的 OAuth 流程在首次实施时都存在一些问题,需要调试。
在我调试 OAuth 流程时 Randall 是如何观察我的?XKCD
某些流程也会因看似随机的原因而中断:例如,如果你传入 PKCE 参数,LinkedIn OAuth 就会中断。你得到的错误?“客户端错误 - 无效的 OAuth 请求。” 那是……告诉?我们花了一个小时才明白传递(可选,通常被忽略)PKCE 参数是中断流程的原因。
另一个常见错误是发送的范围与你在应用程序中预注册的范围不匹配。(预注册范围?是的,现在很多 API 都需要这样做。)这通常会导致出现有关范围存在问题的一般错误消息。呃。
事实是,如果你通过使用他们的 API 构建其他系统,你可能处于较弱的位置。你的客户要求集成,因为他们已经在使用其他系统。现在你需要让他们开心。
公平地说,许多 API 都是自由的,并为开发人员提供简单的自助服务注册流程来注册他们的应用程序并开始使用 OAuth。但是一些最流行的 API 在你的应用程序公开并且可以被任何用户使用之前需要进行审查。同样,公平地说,大多数审核过程都很正常,可以在几天内完成。就最终用户的安全性和质量而言,它们可能是净收益。
但一些臭名昭著的例子可能需要数月才能完成,有些甚至需要你签订收入分成协议:
随着攻击被发现,可用的 Web 技术不断发展,OAuth 标准也发生了变化。如果你希望实施当前的安全最佳实践,OAuth 工作组为你提供了一份相当冗长的指南。如果你使用的 API 目前仍在使用 OAuth 1.0a,你就会意识到向后兼容性是一场永无止境的斗争。
幸运的是,安全性随着每次迭代而变得更好,但这通常是以开发人员的更多工作为代价的。即将推出的 OAuth 2.1 标准将使一些当前的最佳实践成为强制性的,包括强制性的 PKCE(目前只有少数 API 需要这个)和对刷新令牌的额外限制。
至少 OAuth 已经实现了双因素身份验证模型。XKCD
随着访问令牌的过期和刷新令牌的兴起,可能已经迎来了最大的变化。从表面上看,这个过程似乎很简单:每当访问令牌过期时,用刷新令牌刷新它并存储新的访问令牌和刷新令牌。
实际上,当我们实现这个时,我们必须考虑:
遗憾的是,我们只是触及了 OAuth 实施的皮毛而已。现在,OAuth 流程已经运行并且获得了访问令牌,这时候需要考虑:
原文链接:https://www.nango.dev/blog/why-is-oauth-still-hard