OAuth 2中隐式授权类型的目的是什么?

289
我不知道是否只是我有某种盲点,但我已经多次阅读了OAuth 2规范并查看了邮件列表归档,但我尚未找到一个好的解释为什么开发了Implicit Grant flow以获取访问令牌。与Authorization Code Grant相比,它似乎只是放弃了客户端身份验证,没有非常引人注目的原因。这个“针对在使用脚本语言实现的浏览器中的客户端进行优化”(引用规范)是如何实现的呢?
这两种流程都是从相同的起点开始的(来源:https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-22):
1. 客户端通过将资源所有者的用户代理重定向到授权终端点来启动流程。 2. 授权服务器通过用户代理对资源所有者进行身份验证,并确定资源所有者是否授予或拒绝客户端访问请求。 3. 假设资源所有者授予访问权限,则授权服务器使用之前提供的重定向URI(在请求或客户端注册期间)将用户代理重定向回客户端。
重定向URI分别包含授权代码(Authorization code flow)和访问令牌(Implicit flow)。
这里是流程分叉的地方。在这两种情况下,此时重定向URI指向客户端托管的某个端点:
1. 在Authorization code flow中,当用户代理使用URI中的Authorization code命中该端点时,该端点上的代码将授权代码与其客户端凭据交换以获取访问令牌,然后可以根据需要使用它。例如,它可以将其写入页面,页面上的脚本可以访问它。 2. Implicit flow完全跳过了此客户端身份验证步骤,并仅加载带有客户端脚本的Web页面。这里有一个URL片段的巧妙技巧,使访问令牌不会被传递太多次,但最终结果基本相同:客户端托管的站点提供了一个带有一些脚本的页面,可以抓取访问令牌。因此我的问题是:跳过客户端认证步骤有什么收获?

请查看此链接:http://www.ibm.com/developerworks/wikis/display/tivolifederatedidentitymanager/Example+of+running+an+implicit+grant+OAuth+2.0+flow - Håvard Geithus
5
之前的评论中的链接已经失效。这是更新后的链接 - AndrewR
3
我已经阅读了所有的回答,但我仍然不明白为什么在获取访问令牌时不需要私有客户端密码就能够确保安全。比如说TrustedAppDeveloper发布了TrustedPopularApp,它允许用户使用Twitter oauth授权来授予权限。如果我是EvilAppDeveloper,那么有什么方法可以阻止我制作一个应用程序,在隐式授权请求中将TrustedPopularAppId作为client_id传递,然后以用户的身份执行操作(如垃圾邮件),这些操作现在看起来像是来自TrustedPopularApp? - adevine
我和adevine一样也很好奇。但是,大多数需要隐式授权请求的应用程序不需要更多的身份验证,因为它们都是获取操作。 - Mave
15
在您的情境中,什么会阻止邪恶应用程序作为受信任的热门应用程序进行 Twitter 认证,是因为它无法接收来自 Twitter 的回调,这些回调始终会发送到注册客户端 ID 时定义的 URI。 - Ivan
1
可能是这两个工作流有什么区别?何时使用授权代码流程?的重复问题。 - Michael Freidgeim
13个回答

214

这是我的想法:

授权码流程中认证代码和令牌的目的在于,由于它们是服务器到服务器传输的,因此资源所有者永远不会暴露令牌和客户端密钥。

另一方面,隐式授权流程适用于完全使用JavaScript实现并在资源所有者的浏览器中运行的客户端。您不需要任何服务器端代码来使用此流程。然后,如果一切都发生在资源所有者的浏览器中,那么再发放认证代码和客户端密钥就没有任何意义,因为令牌和客户端密钥仍将与资源所有者共享。包括认证代码和客户端密钥只会使流程更加复杂,而不会增加任何真正的安全性。

因此,“得到了什么?”的答案是“简化”。


4
谢谢。在授权代码流程中,资源所有者永远不需要看到访问令牌,而在JavaScript客户端中却是不可避免的。然而,在授权代码流程中仍然可以向JavaScript客户端隐藏客户端密钥:在认证和获取访问令牌后,服务器端代码将传递令牌给JavaScript客户端。不过我现在看到的是,隐式授予流程使得像Facebook这样的javascript oauth SDK的分发成为可能,从而使开发人员完全摆脱编写自己的oauth代码的负担。 - Dan Taflin
3
我可能会补充一下,授权码流程使客户端能够存储令牌并重复使用它们。在隐式流程中,您并不总是有这个选项,因此,隐式流程是安全级别和便利性之间的实用选择。 - PålOliver
2
这只回答了一半,“失去了什么”呢? - EralpB
4
我认为这不是一个全面的答案,隐式流并不旨在通过简单获取优势,而是通过客户端应用程序来折衷安全问题。 "授权码",连同 "client_id" 和 "client_secret" 一起用于识别可信客户端,该客户端可以刷新令牌以进行长时间登录和"离线登录"。然而,在客户端应用程序中,没有办法注册每个客户端,因此采用了“简化”的隐式授权类型,以获得对用户信息的临时访问权限。 - Chen Xie
2
包括客户端密钥不仅使流程更加复杂,而且会使其不够安全。如果客户端密钥需要在客户端代码中枚举,则它不再是秘密,因此将被暴露于互联网。如果您的客户ID仅用于隐式流程,则这不是一个问题。但是,如果它在您的平台上的其他地方也用于刷新令牌或授权码授予,则相应的密钥被公开是一个大问题。 - rurouniwallace
显示剩余4条评论

102

它存在于安全原因而非简单性。

您应考虑用户代理客户端之间的区别:

用户代理是资源所有者与系统的其他部分(认证服务器和资源服务器)进行通信的软件。

客户端是想要访问用户在资源服务器上资源的软件。

对于解耦的用户代理和客户端,授权码授予是有意义的。例如,用户使用Web浏览器(用户代理)在Kickstarter上使用其Facebook帐户登录。在这种情况下,客户端是Kickstarter的一台服务器,该服务器处理用户登录。该服务器从Facebook获取访问令牌和刷新令牌。因此,这种类型的客户端被认为是“安全”的,因为受到访问限制,可以保存令牌,Kickstarter可以访问用户资源并甚至刷新访问令牌而无需用户交互。

如果用户代理和客户端耦合在一起(例如本地移动应用程序、JavaScript应用程序),则可以应用隐式授权工作流。它依赖于资源所有者的存在(用于输入凭据),不支持刷新令牌。如果此客户端存储访问令牌以供以后使用,这将是安全问题,因为令牌可以轻松地被其他应用程序或客户端的用户提取。缺少刷新令牌是另一个提示,表明此方法不适用于在用户缺席时访问用户资源。


2
我发现我的浏览器已经登录了我的 Google 账户好几个月了。那么 Google 是在浏览器上使用访问令牌还是一个具有长时间过期时间的访问令牌?长时间过期的访问令牌和访问令牌的使用方式有什么区别?任何其他客户端都可以捕获访问令牌并在资源所有者不在场时使用它。 - Mohammad Nikravan
我猜你是指“刷新令牌”和“长期过期的访问令牌”的区别吧?在不安全的情况下,不应该保存刷新令牌,但可以保存访问令牌(例如,在浏览器的本地存储中)。通过尽可能将访问令牌的生命周期保持低,同时对用户来说还是舒适的(例如,在x分钟的不活动后自动注销),可以实现安全性。如果使用长寿命的访问令牌,你实际上会使刷新令牌过时。 - artkoenig
谢谢你的解释,但我仍有另一个疑问。我不明白为什么我们需要“授权码”流。我们可以通过隐式流(access_token)和刷新令牌在服务器上达到相同的结果。看起来,隐式流的唯一安全考虑是access_code应该有短暂的生命周期,以便不能在服务器之间使用。但是,刷新令牌解决了这个问题。为什么我们应该使用auth_code流并请求使用该令牌的access_token,在服务器上获取access_code,而我们可以使用refresh_token实现相同的结果呢? - Mohammad Nikravan
"令牌可以被其他应用程序轻松提取" 如何做到? - mvmn
@MohammadNikravan,请在 https://dev59.com/fGYr5IYBdhLWcg3wytLe 中寻找答案。 - Ilya Serbis

67
通常的解释是,当你使用JavaScript客户端请求受保护资源时,隐式授权更容易实现。但我认为这种看法是错误的。如果你正在使用一个通过XMLHttpRequest直接请求受保护资源的JavaScript客户端,那么隐式授权是你唯一的选择,尽管它不太安全。
授权码授权提供了额外的安全性,但只有在有Web服务器请求受保护资源时才有效。由于Web服务器可以存储访问令牌,所以你运行的风险较小,而且可以发行长时间的令牌。由于Web服务器是可信任的,所以它可以被赋予"刷新令牌",因此当旧令牌过期时,它可以获取新的访问令牌。
但是——这是一个容易忽略的点——只有在Web服务器受到会话保护(即登录)时,授权码流的安全性才能生效。没有会话,一个不受信任的用户可以使用client_id向Web服务器发出请求,这就像用户拥有访问令牌一样。添加会话意味着只有经过身份验证的用户才能访问受保护资源。client_id只是JS webapp的身份,而不是该webapp的身份验证。
这也意味着,在OAuth标记过期之前,您可以结束会话。没有一种标准的方法来使访问令牌失效。但是如果你的会话过期了,那么访问令牌就是无用的了,因为只有Web服务器知道它。如果一个不受信任的用户获取了你的会话密钥,他们只能在会话有效期内访问受保护资源。
如果没有Web服务器,则必须使用隐式授权。但这意味着访问令牌暴露在互联网上。如果一个不受信任的用户获得了访问令牌,他们可以在其过期之前使用它。这意味着他们将比授权码授权更长时间地拥有访问权限。因此,你可能希望考虑使令牌更快地过期,并避免给予对更敏感资源的访问。

*编辑: 最近,人们建议即使在没有服务器的Web应用程序中,也要避免使用隐式授权。相反,您可以使用配置了空密钥的授权代码授予以及PKCE。授权码授予避免将访问令牌存储在浏览器历史记录中,而PKCE则避免在某人劫持重定向URL以窃取授权代码时泄露它。在这种情况下,您需要服务器来避免返回刷新令牌,因为您的客户端可能无法安全地存储它。并且它应该发放具有上述相同限制的访问令牌。


22

总的来说:如果用户运行基于浏览器或“公共”的(JavaScript)网络应用程序没有服务器端组件,那么用户会隐含地相信该应用程序以及在其中运行的浏览器,可能还有其他基于浏览器的应用程序...

不存在第三方远程服务器,只有资源服务器。授权码没有好处,因为除了浏览器代表用户操作之外,没有其他代理。客户端凭据也没有好处,原因同上。(任何客户端都可以尝试使用此流程。)

然而,安全方面的影响非常重要。来自https://www.rfc-editor.org/rfc/rfc6749#section-10.3

使用隐式授权类型时,访问令牌在 URI 片段中传输,这可能会将其暴露给未经授权的人。

来自https://www.rfc-editor.org/rfc/rfc6749#section-10.16

资源所有者可以通过向攻击者的恶意客户端授予访问令牌来自愿委托访问资源。这可能是由于钓鱼或其他某些借口...


“public”是什么意思?(JavaScript)没有服务端组件的Web应用程序?怎么可能有没有服务器的Web应用程序? - ANewGuyInTown
2
@ZammyPage,这通常被称为单页应用程序(SPA)。整个应用程序都是从静态资源中提供的。然后应用程序中的JavaScript动态访问它需要的任何资源,并在可以访问的任何资源服务器上进行访问。没有服务器生成客户端内容:客户端中的JavaScript根据需要修改DOM以表示已访问的资源。 - Elroy Flynn
有一个简单但意义深远的好处:如果您存储服务器日志并使用授权码流程,如果日志泄漏,则所有代码很可能都无效。但是,如果您存储访问令牌,则可以直接模拟用户会话。 - Délisson Junio

14

我不确定我是否正确地理解了答案和丹的评论。在我看来,答案陈述了一些事实,但并没有完全指出OP所问的内容。如果我理解正确,隐式授权流的主要优点是像JS应用程序(例如Chrome扩展)这样的客户端无需公开客户端密钥。

Dan Taflin说:

...在授权码流中,资源所有者永远不需要看到访问令牌,而在javascript客户端中,这是无法避免的。然而,使用授权码流仍然可以将客户端秘密保留在javascript客户端之外。

也许我误解了您的意思,但是客户端(在此情况下为JS应用程序)必须向资源服务器传递客户端凭据(客户端密钥和密码),对吗?客户端秘密无法“保密于JS”。


6
我知道这是一个老问题,但这个答案比被采纳的答案更好。隐式授权存在的原因是javascript客户端无法保密,因此无法进行身份验证。因此,授权服务器必须仅依靠重定向URI注册和用户代理来确保安全。您只能将授权令牌传递给用户代理,并且仅在特定的重定向URI上,从理论上讲可以防止拦截(因为恶意用户无法在该URI的用户代理中执行代码,他不拥有重定向URI的域)。 - gregates
事实上,被采纳的回答让我感到困惑。它让我觉得我可能误解了 client_secret 的含义!这个回答和上面的评论都是正确的。 - Sarsaparilla

10

虽然Implicit Grant旨在支持不能保护客户端密钥(包括客户端JavaScript应用程序)的应用程序,但一些提供者正在实现使用授权代码而无需客户端密钥的替代方案。OAuth 2.0 IETF RFC-6749于2012年发布,当前推荐的一些最新讨论来自于2017年。

关于IETF OAuth邮件列表的2017年讨论可从以下实施者处获得:

了解更多信息,请点击此处:

以前建议没有密钥的客户端使用"隐式授权",但现在已经被使用不带密钥的授权码授权所取代。
……
以前,推荐基于浏览器的应用程序使用"隐式"流程,该流程立即返回访问令牌,并且没有令牌交换步骤。自规范最初编写以来,行业最佳实践已经改变,推荐使用不带客户端密钥的授权码流程。这提供了更多创建安全流程的机会,例如使用状态参数。参考文献:RedhatDeutsche TelekomSmart Health IT
在此处还提到了将移动应用从隐式授权转移到不带客户端密钥的授权码授权。

我认为你在这个建议上需要小心。这个建议是针对原生应用程序而不是单页应用程序的指南推荐的。不幸的是,关于单页应用程序的指导并没有得到很好的记录,这在许多在线讨论、论坛甚至oauth-wg邮件列表中都有所体现。 - tstojecki
建议从隐式授权转移到不带密钥的授权码对于单页应用程序(SPAs)和移动应用程序都是一种建议,但我上面的摘录是针对SPAs的。所引用的文章在SPAs和移动应用程序中使用类似的文本,但在各自的文本中使用“基于浏览器的应用程序”、“移动和本机应用程序”的语言。此外,Redhat、DT、Smart Health IT的参考文献都是针对SPAs的,并未包含在移动应用程序的注释中。我已经添加了一个深度链接到SPAs的答案,以便更容易查找。请发布一些您提到的讨论链接。 - Grokify
可以在此处找到一个相当近期的(2018年)oauth-wg讨论:https://www.ietf.org/mail-archive/web/oauth/current/msg18020.html。 RFC 8252是针对本地应用程序的,正如标题所示“OAuth 2.0 for Native Apps”。 提到Redhat、DT、Smart Health IT等的参考资料是回应邮件列表讨论的,而不是RFC、工作草案等。 - tstojecki

5
在隐式流程中,如果用户的浏览器被损坏(恶意扩展程序/病毒),那么损坏就可以访问用户的资源并进行恶意操作。
而在认证流程中,由于不知道客户端密钥,因此损坏无法获得这种权限。

4

除了其他回答之外,还需要意识到隐式配置文件允许前通道流而不是授权代码流,后者需要回调到授权服务器;这在建立在Auth 2.0之上的单点登录协议OpenID Connect中变得明显,其中隐式流类似于非常流行的SAML POST绑定,授权代码流类似于较少部署的SAML Artifact绑定。


3

https://www.rfc-editor.org/rfc/rfc6749#page-8

隐式授权

隐式授权是一种简化的授权码流程,适用于使用脚本语言(如JavaScript)在浏览器上实现的客户端。在隐式流程中,客户端不会被授予授权码,而是直接获得访问令牌(作为资源所有者授权的结果)。授权类型为隐式授权,因为不会发放任何中间凭证(例如授权码),也不会后续用于获取访问令牌。

在隐式授权流程中,授权服务器不会对客户端进行身份验证。在某些情况下,可以通过用于向客户端传递访问令牌的重定向URI来验证客户端身份。访问令牌可能会暴露给资源所有者或其他具有访问权限的应用程序。

隐式授权提高了某些客户端(例如作为浏览器应用程序实现的客户端)的响应速度和效率,因为它减少了获取访问令牌所需的往返次数。


2

我认为Will Cain已经回答了这个问题,他说:“出于同样的原因,客户端凭据没有任何好处。(任何客户端都可以尝试使用此流程。)”同时考虑到隐式流的redirect_uri可能是“localhost”——授权服务器不会为隐式流进行回调。由于没有办法预先信任客户端,用户必须批准发布用户声明。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接