什么是适合移动应用的OAuth 2.0流程?

109

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

根据规范,对于在浏览器上运行的一些JavaScript代码来说,这似乎是一个不错的场景。我试图最小化用户必须授予应用程序以获得令牌的次数,因此看起来授权码流程是一个很好的选择,因为它支持刷新令牌。

然而,这个流程似乎严重依赖于用于执行重定向的Web浏览器。如果使用嵌入式Web浏览器,我想知道这个流程是否仍然是适合移动应用程序的好选择。还是我应该选择隐式流程?


1
问题是 - 用户是否永远不需要在第一次登录后再次输入密码,这是最高优先级的要求吗? - leastprivilege
是的,那正是我的需求。用户只需输入一次密码。但是,我不想在移动应用程序中设置具有无限生命周期并将其保留在令牌中,因为这会违反撤销令牌的能力。(除非我在移动应用程序中添加一些逻辑来检测请求未经授权,以便在此之后请求新令牌) - Pablo Cibraro
1
您可以添加一个无限期的令牌,但仍然可以撤销它。是的,应用程序逻辑应该能够检测到这一点。RFC 6750定义了一种检查错误是否由已撤销的令牌引起的方法。 - Pedro Felix
1
请避免使用 Web 视图(除非您拥有完整的堆栈并且未使用社交登录),这会打开密码被攻击的可能性。当第三方嵌入式用户代理要求我的凭据时,我会卸载该应用程序。现在一些 API 甚至禁止此类集成,例如 https://dev.fitbit.com/docs/oauth2/。我已经提供了另一个答案以进一步澄清这些概念(https://dev59.com/rWQm5IYBdhLWcg3wrAiY#38582630)。 - Matt C
5个回答

110

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

正如其他评论和一些在线来源所述,隐式似乎是移动应用程序的自然选择,但最佳解决方案并不总是明确的(实际上,由于下面讨论的原因,隐式不被推荐)。

原生应用程序 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/

授权码授权流程适用于具有 Web 服务的应用程序。此流程需要使用应用程序的客户端密钥进行服务器之间的通信。
注意:永远不要将您的客户端秘密放在分发代码中,例如通过应用商店下载的应用程序或客户端 JavaScript。
没有 Web 服务的应用程序应该使用隐式授权流程。
结论:
最终决策应考虑您所期望的用户体验,但也应在对入围方法进行适当的风险评估并更好地了解其影响后考虑风险承受能力。
这里有一篇很棒的文章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视图注意事项

在野外有很多使用Web视图(即嵌入式用户代理)的示例,但应避免使用此方法(特别是当应用程序不是第一方时),在某些情况下可能会导致您被禁止使用API,如here所示。

任何试图嵌入OAuth 2.0认证页面的尝试都将导致您的应用程序被禁止使用Fitbit API。出于安全考虑,OAuth 2.0授权页面必须在专用浏览器视图中呈现。只有通过浏览器提供的工具(如URL栏和传输层安全性(TLS)证书信息),Fitbit用户才能确认他们正在与真正的Fitbit.com网站进行身份验证。对于本机应用程序,这意味着授权页面必须在默认浏览器中打开。本机应用程序可以使用自定义URL方案作为重定向URI,以将用户从浏览器重定向回请求权限的应用程序。iOS应用程序可以使用SFSafariViewController类而不是切换到Safari。禁止使用WKWebView或UIWebView类。Android应用程序可以使用Chrome自定义选项卡,而不是切换到默认浏览器。禁止使用WebView。为进一步澄清,这里是上面提供的最佳实践链接的先前草案的this section引用。
嵌入式用户代理通常使用 Web 视图实现,是授权本机应用程序的另一种方法。但从定义上来说,它们对于第三方使用是不安全的。它们要求用户使用完整的登录凭据进行登录,然后将其降级为权限较低的 OAuth 凭据。即使由可信任的第一方应用程序使用,嵌入式用户代理也会通过获得比其所需更强大的凭证来违反最小特权原则,从而可能增加攻击面。在典型的基于 Web 视图的嵌入式用户代理实现中,主机应用程序可以记录表单中输入的每个按键,以捕获用户名和密码;自动提交表单并绕过用户同意;复制会话 cookie 并将其用于作为用户执行经过身份验证的操作。鼓励用户在没有浏览器通常具有的地址栏和其他身份特征的嵌入式 Web 视图中输入凭据,使用户无法知道他们是否正在登录到合法网站,即使他们是,也会让他们训练成可以在未验证站点的情况下输入凭据的做法。除了安全问题外,Web 视图不与其他应用程序或系统浏览器共享身份验证状态,需要用户为每个授权请求登录,从而导致用户体验差。因此,除非可信任的第一方应用程序充当其他应用程序的外部用户代理或为多个第一方应用程序提供单点登录,否则不建议使用嵌入式用户代理。授权服务器应考虑采取措施来检测和阻止不属于它们自己的嵌入式用户代理的登录,如果可能的话。

这里还提出了一些有趣的观点:为什么开发人员使用嵌入式用户代理进行第三方认证?有哪些原因?https://security.stackexchange.com/questions/179756/why-are-developers-using-embedded-user-agents-for-3rd-party-auth-what-are-the-a


3
谷歌将于2017年4月20日停止支持网页视图,详情请见 https://developers.googleblog.com/2016/08/modernizing-oauth-interactions-in-native-apps.html?m=1。 - Matt C
请注意,本答案开头提到的文档不再是草稿:OAuth 2.0原生应用程序 - https://tools.ietf.org/html/rfc8252 - Kostiantyn Sokolinskyi
感谢@KostiantynSokolinskyi,已根据不再是草案的rfc链接进行了编辑。 - Matt C
@MattC,实现新用户注册的最佳方式是什么?我们应该在应用程序内部还是在IDP上进行?是否可以在注册后自动登录用户?https://stackoverflow.com/questions/60187173/oidc-register-auto-login-flow-for-mobile-apps - Yashvit
抱歉我对细节有些疑惑...你能帮忙看一下吗?谢谢!链接---> https://stackoverflow.com/q/61313694/4619958 - ch271828n

29

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

  • 如果可以要求用户提供他/她的凭据,则使用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://...),当解引用时会触发注册的“处理程序”(详细信息取决于移动平台)。
    • 如果可用,则使用特殊的“Web视图”,例如Windows 8上的WebAuthenticationBroker,以控制和访问HTTP重定向响应。

希望这有所帮助

Pedro


1
这完全取决于您是否希望客户在 Web 视图中输入密码还是在客户端应用程序中输入密码。如果可能的话,我更喜欢客户端应用程序 - 然后立即使用访问/刷新令牌交换密钥。 - leastprivilege
谢谢Dominick!我的客户正在使用ADFS对用户进行身份验证,因此他们希望在登录页面中输入凭据。Web视图将为他们工作。 - Pablo Cibraro
5
我好奇你为什么会推荐“授权码流程”?难道不需要客户端密钥和客户端ID 来交换code以获取access_token吗?我认为“隐式”流程是为这些场景设计的,因为它不需要将密钥存储在设备中。 - Eugenio Pace
1
隐式授权不支持刷新令牌OOB。在Pablo的情况下 - 我明确建议RO流程。听起来像是公司部署了针对同一公司后端的应用程序。 - leastprivilege
@EugenioPace 在这里是正确的。 请避免使用打开密码泄露可能性的 Web 视图。当嵌入式用户代理要求凭据时,我会卸载应用程序。一些 API 现在甚至禁止此类集成,例如 https://dev.fitbit.com/docs/oauth2/。 我已经提供了另一个答案来进一步澄清这些概念(https://dev59.com/rWQm5IYBdhLWcg3wrAiY#38582630)。 - Matt C
显示剩余5条评论

11

TL;DR: 使用带有PKCE的授权码许可进行身份验证。

1. 隐式授权类型

隐式授权类型在移动应用程序中非常受欢迎。但它并不是为这种情况而设计的。重定向存在安全问题。Justin Richer指出

问题在于,与远程服务器URL不同,在给定的重定向URI和特定移动应用程序之间没有可靠的方法来确保绑定得到尊重。设备上的任何应用程序都可以尝试将其插入重定向过程并导致其提供重定向URI。猜猜怎么着:如果您已在本机应用程序中使用了隐式流程,则刚刚把访问令牌交给了攻击者。从那一点开始就没有恢复了-他们拥有了令牌,可以使用它。

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

2. 授权码授权类型

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

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

虽然这不是理想的解决方案,但现在有一个更好的方法来在移动设备上执行OAuth:证明代码交换(PKCE)。

3. 使用PKCE的授权码授权类型

通过克服限制,创建了一种新技术,使您可以在没有客户机密的情况下使用授权码。您可以阅读完整的RFC 7636此简短介绍
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) {}

3
OAuth2的最佳实践:使用原生应用程序 https://tools.ietf.org/html/draft-wdenniss-oauth-native-apps - Matt C
1
正如Matt C所说,Web Views对于移动应用程序来说是一个糟糕的想法 - 它们不安全,允许应用程序获取凭据(因此不比RO更安全),并且不允许用户验证域和TLS证书。使用自定义URI处理程序的Auth Code授权类型,并确保您正在使用Proof Code for Key Exchange(PKCE)来防止手机上的恶意应用程序拦截授权代码并获得对API的访问权限。 - ChrisC
2
OAuth 2.0本地应用最佳实践文档的更新版本位于https://tools.ietf.org/html/draft-ietf-oauth-native-apps。 - Jeff Olson

-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。


在提供便利的用户体验时,我们将看到该行业摒弃这种方法。由于Web视图可能会危及密码安全,当嵌入式用户代理要求我的凭据时,我会卸载该应用程序。现在,一些API甚至禁止此类集成,例如:https://dev.fitbit.com/docs/oauth2/。 - Matt C
OAuth2 的本地应用最佳实践: https://tools.ietf.org/html/draft-wdenniss-oauth-native-apps - Matt C
我不认为一个启用了 oauth 的服务会禁止这种方法。这是无法被检测到的,也是安全的。一些启用了 oauth 的服务提供平台特定的客户端来简化身份验证,这样的客户端实际上就是我在此描述的内容(显示嵌入式 webview 并跟踪 URL 更改)。你提供的最佳实践建议与我的说法相同:使用系统浏览器或嵌入式 webview。你从我的回复中攻击了哪个论点?不清楚。 - Radu Simionescu
没有任何攻击意图,只是强调这个问题。该链接提到了您所提到的两种方法,但只有外部用户代理可以被认为是安全的,具体而言,原生应用程序的选项是“通过嵌入式用户代理或外部用户代理”。本文建议像应用内浏览器标签一样的外部用户代理作为OAuth的唯一安全且可用的选择。 - Matt C
进一步引用:“在嵌入式用户代理的典型基于Web视图的实现中,主机应用程序可以:记录在表单中输入的每个按键以捕获用户名和密码;自动提交表单并绕过用户同意”……“不建议使用嵌入式用户代理,除非可信任的第一方应用程序充当其他应用程序的外部用户代理或为多个第一方应用程序提供单点登录。授权服务器应考虑采取措施检测和阻止通过非自己的嵌入式用户代理进行的登录,如果可能的话。” - Matt C
OAuth 2.0本地应用最佳实践文档的更新版本位于https://tools.ietf.org/html/draft-ietf-oauth-native-apps。 - Jeff Olson

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