什么是移动应用程序的正确 OAuth 2.0 流程

Posted

技术标签:

【中文标题】什么是移动应用程序的正确 OAuth 2.0 流程【英文标题】:What's the right OAuth 2.0 flow for a mobile app 【发布时间】:2013-06-29 22:41:54 【问题描述】:

我正在尝试在使用 OAuth 2.0 的移动应用程序的 Web API 中实现委托授权。根据规范,隐式授权流程不支持刷新令牌,这意味着一旦在特定时间段内授予访问令牌,一旦令牌过期或被撤销,用户必须再次授予应用程序权限。

我想这对于在浏览器上运行的一些 javascript 代码来说是一个很好的场景,正如规范中提到的那样。我试图尽量减少用户必须授予应用程序权限以获取令牌的时间,因此看起来授权代码流是一个不错的选择,因为它支持刷新令牌。

但是,此流程似乎严重依赖 Web 浏览器来执行重定向。我想知道如果使用嵌入式 Web 浏览器,此流程对于移动应用程序是否仍然是一个不错的选择。还是我应该使用隐式流程?

【问题讨论】:

问题是——用户第一次登录后再也不用输入密码是不是最高优先级? 是的,这正是我的要求。用户应该只键入一次密码。但是,我不想设置一个具有无限生命周期的令牌并将其保留在移动应用程序中,因为这会违背撤销令牌的能力。 (除非我在移动应用程序中添加一些逻辑来检测该请求未经授权,因此我在此之后请求一个新令牌) 您可以添加一个具有无限生命周期的令牌,但仍然可以撤销它。是的,应用程序逻辑应该能够检测到这一点。 RFC 6750 定义了一种方法来检查错误是否是由于撤销的令牌引起的。 请避免浏览网页(除非您拥有完整的堆栈并且未使用社交登录),这可能会导致密码泄露。当第三方嵌入式用户代理要求我提供凭据时,我会卸载该应用程序。一些 API 现在甚至禁止这样的集成,例如 dev.fitbit.com/docs/oauth2 我提供了另一个答案以进一步阐明其中一些概念 (***.com/a/38582630/752167) 【参考方案1】:

澄清:移动应用 = 原生应用

正如其他 cmets 和一些在线资源中所述,隐式似乎很适合移动应用,但最佳解决方案并不总是明确的(实际上不推荐使用隐式,原因如下所述)。

原生应用 OAuth2 最佳实践

无论您选择哪种方法(需要考虑一些权衡),您都应该注意此处概述的使用 OAuth2 的原生应用的最佳实践:https://www.rfc-editor.org/rfc/rfc8252

考虑以下选项

隐式

我应该使用隐式吗?

引用第 8.2 节https://www.rfc-editor.org/rfc/rfc8252#section-8.2

OAuth 2.0 隐式授权授权流程(在 OAuth 2.0 [RFC6749] 第 4.2 节中定义)通常适用于在浏览器中执行授权请求并通过基于 URI 的应用间通信接收授权响应的做法。 但是,由于 PKCE [RFC7636](第 8.1 节要求)无法保护隐式流,不建议将隐式流与本机应用程序一起使用

通过隐式流程授予的访问令牌在没有用户交互的情况下也无法刷新,从而使授权代码授予流程- 它可以发出刷新令牌——对于需要刷新访问令牌的本机应用授权来说,这是更实用的选择。

授权码

如果您确实使用授权代码,那么一种方法是通过您自己的 Web 服务器组件进行代理,该组件使用客户端密码丰富令牌请求,以避免将其存储在设备上的分布式应用程序中。

以下摘自:https://dev.fitbit.com/docs/oauth2/

建议将授权代码授予流程用于以下应用程序: 有一个网络服务。此流程需要服务器到服务器的通信 使用应用程序的客户端密码。

注意:切勿将您的客户端机密放入分布式代码中,例如应用程序 通过应用商店或客户端 JavaScript 下载。

没有网络服务的应用程序应该使用隐式 拨款流程。

结论

在对入围的方法进行适当的风险评估并更好地了解其影响后,最终决定应考虑您所需的用户体验以及您对风险的偏好。

在这里阅读精彩 https://auth0.com/blog/oauth-2-best-practices-for-native-apps/

另一个是https://www.oauth.com/oauth2-servers/oauth-native-apps/,它表示

当前行业最佳实践是使用授权流程 同时省略客户端密码,并使用外部用户代理 完成流程。外部用户代理通常是设备的 本机浏览器,(与本机应用程序具有单独的安全域,) 使应用程序无法访问 cookie 存储或检查或修改 浏览器内的页面内容。

PKCE 考虑

您还应该考虑这里描述的 PKCE https://www.oauth.com/oauth2-servers/pkce/

具体来说,如果您还实现了授权服务器,那么https://www.oauth.com/oauth2-servers/oauth-native-apps/checklist-server-support-native-apps/ 声明您应该这样做

允许客户端为其重定向 URL 注册自定义 URL 方案。 支持具有任意端口号的环回 IP 重定向 URL 以支持桌面应用程序。 不要假设原生应用程序可以保密。要求所有应用声明它们是公开的还是机密的,并且只向机密应用发布客户端机密。 支持 PKCE 扩展,并要求公共客户端使用它。 尝试检测授权界面何时嵌入到本机应用的 Web 视图中,而不是在系统浏览器中启动,并拒绝这些请求。

网页浏览量注意事项

有很多使用 Web 视图的示例,即嵌入式用户代理,但应避免使用这种方法(尤其是当应用程序不是第一方时),并且在某些情况下可能会导致您被禁止使用 API正如下面来自here 的摘录所示

任何嵌入 OAuth 2.0 身份验证页面的尝试都将导致 您的应用程序被 Fitbit API 禁止。

出于安全考虑,OAuth 2.0 授权页面必须是 呈现在专用的浏览器视图中。 Fitbit 用户只能确认 他们正在使用真正的 Fitbit.com 网站进行身份验证,如果他们有 浏览器提供的工具,例如 URL bar 和 Transport 层安全 (TLS) 证书信息。

对于原生应用,这意味着授权页面必须打开 在默认浏览器中。本机应用程序可以使用自定义 URL 方案 作为重定向 URI 将用户从浏览器重定向回 申请许可。

ios 应用程序可以使用 SFSafariViewController 类而不是 应用程序切换到 Safari。 WKWebView 或 UIWebView 类的使用是 禁止。

android 应用程序可能会使用 Chrome 自定义选项卡而不是应用程序 切换到默认浏览器。禁止使用 WebView。

为了进一步澄清,这里引用了this section 以上提供的最佳实践链接的先前草案

嵌入式用户代理,通常通过网络视图实现,是一种 授权本机应用程序的替代方法。然而他们是 根据定义,第三方使用不安全。它们涉及用户 使用他们的完整登录凭据登录,只是为了让他们 缩小到功能较弱的 OAuth 凭据。

即使由受信任的第一方应用程序使用,嵌入式用户代理 通过获得更大的权力来违反最小特权原则 凭据比他们需要的多,可能会增加攻击面。

在典型的基于 Web 视图的嵌入式用户代理实现中, 主机应用程序可以: 将表单中输入的每个按键记录到 捕获用户名和密码;自动提交表单并绕过 用户同意;复制会话 cookie 并使用它们来执行 以用户身份进行身份验证的操作。

鼓励用户在嵌入式 Web 视图中输入凭据,而无需 浏览器具有的常用地址栏和其他身份功能 使用户无法知道他们是否正在登录 合法网站,即使他们是,它也会训练他们没关系 无需先验证网站即可输入凭据。

除了安全问题,网络视图不共享 与其他应用程序或系统浏览器的身份验证状态,需要 用户登录每个授权请求并导致 用户体验差。

由于上述原因,不建议使用嵌入式用户代理, 除非受信任的第一方应用程序充当外部用户 - 其他应用程序的代理,或为多个首次提供单点登录 派对应用。

授权服务器应该考虑采取措施来检测和阻止 通过不属于他们自己的嵌入式用户代理登录,其中 可能。

这里也提出了一些有趣的观点:https://security.stackexchange.com/questions/179756/why-are-developers-using-embedded-user-agents-for-3rd-party-auth-what-are-the-a

【讨论】:

Google 将于 2017 年 4 月 20 日取消对 webviews 的支持developers.googleblog.com/2016/08/… 仅供参考,如果不再草拟本答案开头的文档引用,OAuth 2.0 for Native Apps - tools.ietf.org/html/rfc8252 感谢@KostiantynSokolinskyi,相应地编辑了不再是草稿的 rfc 链接 @MattC 实现新用户注册的最佳方式是什么?我们应该在应用程序内还是在 IDP 上进行?是否可以自动登录用户发布注册? ***.com/questions/60187173/… 对不起,我对一些细节感到困惑......你能看看吗?谢谢!链接--->***.com/q/61313694/4619958【参考方案2】:

很遗憾,我认为这个问题没有明确的答案。但是,以下是我确定的选项:

如果可以询问用户他/她的凭据,则使用Resource Owner Password Credentials。但是,由于某些原因,这可能是不可能的,即

可用性或安全策略禁止直接在应用中插入密码 身份验证过程委托给外部身份提供者,并且必须通过基于 HTTP 重定向的流程(例如 OpenID、SAMLP 或 WS-Federation)执行

如果需要使用基于浏览器的流程,请使用Authorization Code Flow。在这里,redirect_uri 的定义是一个重大挑战,有以下选择:

使用https://developers.google.com/accounts/docs/OAuth2InstalledApp 中描述的技术,其中特殊的redirect_uri(例如urn:ietf:wg:oauth:2.0:oob)向授权端点发出信号以显示授权代码,而不是重定向回客户端应用程序。用户可以手动复制此代码,也可以让应用尝试从 HTML 文档标题中获取。 在设备上使用localhost 服务器(端口管理可能不容易)。 使用自定义 URI 方案(例如 myapp://...),在取消引用时会触发已注册的“处理程序”(详细信息取决于移动平台)。 如果可用,请使用特殊的“网络视图”(例如 Windows 8 上的 WebAuthenticationBroker)来控制和访问 HTTP 重定向响应。

希望对你有帮助

佩德罗

【讨论】:

感谢佩德罗的意见!。是的,看起来带有自定义 URI 方案的授权代码流或 Web 视图似乎是这里的最佳选择。 这完全取决于您是否希望客户端将密码输入到 Web 视图或客户端应用程序中。如果可能的话,我更喜欢客户端应用程序 - 然后立即使用访问/刷新令牌交换密钥。 谢谢多米尼克!我的客户正在使用 ADFS 对用户进行身份验证,因此他们想在登录页面中输入凭据。网络视图将适用于他们 我很好奇你为什么会推荐“授权码流”?您不需要client_secret 和client_id 来交换access_token 的代码吗?我认为“隐式”流程是为这些场景设计的,因为它不需要将机密存储在设备中。 隐式不支持刷新令牌 OOB。在 Pablo 的场景中 - 我显然会推荐 RO 流程。听起来像是公司针对同一个公司后端部署的应用程序。【参考方案3】:

TL;DR:将授权码授予与PKCE一起使用

1.隐式授权类型

隐式授权类型在移动应用中非常流行。但它不应该像这样使用。重定向存在安全问题。 Justin Richer states:

当您意识到与远程服务器不同时,问题就来了 URL,没有可靠的方法来保证a之间的绑定 给定的重定向 URI 并尊重特定的移动应用程序。任何 设备上的应用程序可以尝试将自己插入重定向 进程并使其提供重定向 URI。猜猜看:如果 你已经在你的原生应用程序中使用了隐式流,那么你 只是把你的访问令牌交给了攻击者。没有恢复 那一点 — 他们已经拿到了令牌,他们可以使用它。

再加上它不允许你刷新访问令牌,最好避免它。

2。授权码授予类型

授权码授予需要客户端密码。但是您不应该在移动应用程序的源代码中存储敏感信息。人们可以提取它们。为了不暴露客户端机密,您必须以Facebook writes 的身份运行服务器作为中间人:

我们建议应用访问令牌只能直接从 您的应用程序的服务器,以提供最佳的安全性。对于本地人 应用程序,我们建议该应用程序与您自己的服务器通信,并 然后服务器使用应用程序向 Facebook 发出 API 请求 访问令牌。

不是一个理想的解决方案,但有一种新的、更好的在移动设备上进行 OAuth 的方法:代码交换的证明密钥

3.带有 PKCE(代码交换证明密钥)的授权代码授予类型

克服了这些限制,我们创建了一种新技术,让您可以在没有客户端密码的情况下使用授权代码。您可以阅读完整的RFC 7636 或this short introduction。

PKCE (RFC 7636) 是一种保护不使用公共客户端的技术 客户机密。

它主要用于本地和移动应用程序,但该技术可以 也适用于任何公共客户。它需要额外的 授权服务器支持,因此仅支持 某些提供商。

来自https://oauth.net/2/pkce/

【讨论】:

【参考方案4】:

在您的移动应用程序中使用 webview 应该是在 Android 平台上实现 OAuth2.0 协议的一种经济实惠的方式。

至于redirect_uri 字段,我认为http://localhost 是一个不错的选择,您不必在应用程序中移植HTTP 服务器,因为您可以覆盖WebViewClient 类中onPageStarted 函数的实现,并且检查url 参数后,停止从http://localhost 加载网页。

public void onPageStarted(final WebView webView, final String url,
        final Bitmap favicon) 

【讨论】:

使用 OAuth2 的原生应用的最佳实践:tools.ietf.org/html/draft-wdenniss-oauth-native-apps 正如上面的 Matt C 所说。 Web 视图对于移动应用程序来说是个坏主意 - 它们不安全,允许应用程序访问凭据(因此不比 RO 更安全)并且不允许用户验证域和 TLS 证书。使用带有自定义 URI 处理程序的 Auth Code 授权类型,并确保您使用 Proof Code for Key Exchange (PKCE) 阻止手机上的恶意应用拦截 auth 代码并访问您的 API。 OAuth 2.0 for Native Apps 最佳实践文档草案的更新版本位于 tools.ietf.org/html/draft-ietf-oauth-native-apps【参考方案5】:

最流畅的身份验证用户体验和最容易实现的方法是在您的应用中嵌入 webview。处理 webview 从身份验证点收到的响应并检测错误(用户取消)或批准(并从 url 查询参数中提取令牌)。 我认为你实际上可以在所有平台上做到这一点。我已经成功地完成了以下工作:ios、android、mac、windows store 8.1 应用程序、windows phone 8.1 应用程序。我为以下服务执行此操作:dropbox、google drive、onedrive、box、basecamp。对于非 Windows 平台,我使用的是 Xamarin,据说它不会公开整个平台特定的 API,但它确实公开了足够多的内容以使这成为可能。因此,即使从跨平台的角度来看,它也是一个非常易于使用的解决方案,而且您不必担心身份验证表单的 ui。

【讨论】:

在提供便捷的用户体验的同时,我们将看到整个行业逐渐远离这种方法。由于网络视图打开了泄露密码的可能性,当嵌入式用户代理要求我提供凭据时,我会卸载该应用程序。一些 API 现在甚至禁止此类集成,例如 dev.fitbit.com/docs/oauth2 使用 OAuth2 的原生应用的最佳实践:tools.ietf.org/html/draft-wdenniss-oauth-native-apps 我不明白启用 oauth 的服务如何禁止这种方法。它是不可检测且安全的......一些启用 oauth 的服务提供特定于平台的客户端以简化身份验证,并且这些客户端实际上执行我在此处描述的操作(显示嵌入式 web 视图并跟踪 url 更改)。您链接的最佳实践建议同样的事情:使用系统浏览器或嵌入式 webview。你从我的回答中攻击什么论点?目前还不清楚。 无意攻击,只是强调问题。该链接说您提到了两种方法,但只有外部用户代理可以被认为是安全的,特别是它说本机应用程序的选项是“通过嵌入式用户代理或外部用户代理。本文档推荐外部像应用内浏览器标签这样的用户代理是 OAuth 唯一安全且可用的选择。” 进一步引用“在典型的基于 Web 视图的嵌入式用户代理实现中,主机应用程序可以:记录在表单中输入的每个键击以捕获用户名和密码;自动提交表单并绕过用户同意“.......”不推荐使用嵌入式用户代理,除非可信的第一方应用充当其他应用的外部用户代理,或为多个第一方应用提供单点登录. 授权服务器应该考虑采取措施,在可能的情况下,通过不属于他们自己的嵌入式用户代理来检测和阻止登录。”

以上是关于什么是移动应用程序的正确 OAuth 2.0 流程的主要内容,如果未能解决你的问题,请参考以下文章

OAuth 2.0 的探险之旅

OAuth 2.0应该用于身份验证超时吗?

Owin OAuth 2.0密码授权流程

到底什么是 OAuth 2.0?

将 OAuth 2.0 用于 Web 服务器应用程序流时无法选择正确的通道

10 分钟理解什么是 OAuth 2.0 协议