了解 Azure Active Directory 中的 OAuth2 隐式授权流(AD)

警告

本内容适用于较旧版本的 Azure AD v1.0 终结点。 为新项目使用 Microsoft 标识平台

OAuth2 隐式授权因在 OAuth2 规范中拥有最长的安全问题列表而臭名昭著。 然而,这是 ADAL JS 实现的方法,也是编写 SPA 应用程序时建议的方法。 这是怎么回事呢? 这一切都是权衡取舍的问题:结果表明,隐式授权是通过浏览器以 JavaScript 方式调用 Web API 的应用程序的最佳方法。

什么是 OAuth2 隐式授权?

典型的 OAuth2 授权代码授予 是使用两个单独端点的授权。 授权终结点用于用户交互阶段,这会导致授权代码。 然后,客户端使用令牌终结点将代码交换为访问令牌,并且通常还会使用刷新令牌。 Web 应用程序需要向令牌终结点提供其自己的应用程序凭据,以便授权服务器能够对客户端进行身份验证。

OAuth2 隐式授权是其他授权类型的变体。 它允许客户端直接从授权终结点获取访问令牌(在使用 OpenId Connect 时获取 id_token),而无需联系令牌终结点或对客户端进行身份验证。 此变体专为在 Web 浏览器中运行的基于 JavaScript 的应用程序而设计:在原始 OAuth2 规范中,令牌在 URI 片段中返回。 这使得令牌位可用于客户端中的 JavaScript 代码,但它保证在重定向到服务器时不会被包含。 在 OAuth2 隐式授予中,授权终结点使用以前提供的重定向 URI 直接向客户端颁发访问令牌。 它的另一个优点是可以避免跨源调用的需求,但如果需要 JavaScript 应用程序联系令牌终结点,则这些调用是必需的。

OAuth2 隐式授权的一个重要特征是此类流永远不会将刷新令牌返回到客户端。 下一部分说明这不是必要的,实际上将是一个安全问题。

OAuth2 隐式授权的适用方案

OAuth2 规范声明已设计隐式授权,以启用用户代理应用程序,也就是说,在浏览器中执行的 JavaScript 应用程序。 此类应用程序的定义特征是,JavaScript 代码用于访问服务器资源(通常是 Web API),并相应地更新应用程序用户体验。 想想 Gmail 或 Outlook Web Access 等应用程序:从收件箱中选择邮件时,只有邮件可视化面板会更改以显示新选择,而页面的其余部分保持不变。 这一特征与传统的基于重定向的 Web 应用形成鲜明对比,其中每个用户交互都会导致完整页面回发和新服务器响应的完整页面呈现。

采用将 JavaScript 方法发挥到极致的方式的应用程序称为单页应用程序(SPA)。 其想法是,这些应用程序仅提供初始 HTML 页面和关联的 JavaScript,所有后续交互都由通过 JavaScript 执行的 Web API 调用驱动。 但是,混合方法(其中应用程序主要是回发驱动的,但偶尔执行 JS 调用)并不常见 - 关于隐式流用法的讨论也与这些方法相关。

基于重定向的应用程序通常通过 Cookie 保护其请求,但这种方法不适用于 JavaScript 应用程序。 Cookie 仅适用于为其生成的域,而 JavaScript 调用可能会定向到其他域。 事实上,这种情况经常会发生:想想那些调用 Microsoft Graph API、Office API、Azure API 的应用程序——它们都位于运行这些应用程序的域之外。 JavaScript 应用程序的趋势是越来越多地放弃后端,转而完全依赖第三方 Web API 来实现其业务功能。

目前,保护对 Web API 调用的首选方法是使用 OAuth2 持有者令牌方法,其中每个调用都附带 OAuth2 访问令牌。 Web API 会检查传入的访问令牌,如果找到所需的权限范围,则授予对所请求操作的访问权限。 隐式流为 JavaScript 应用程序提供了一种便捷的机制,用于获取 Web API 的访问令牌,在 Cookie 方面具有诸多优势:

  • 可以可靠地获取令牌,而无需跨源调用 - 强制注册令牌返回的重定向URI,以保证令牌不被丢失或泄露。
  • 对于所面向的 Web API,JavaScript 应用程序可以获取任意数量的访问令牌,而对域没有限制
  • 会话或本地存储等 HTML5 功能可完全控制令牌缓存和生存期管理,而 Cookie 管理对应用不透明
  • 访问令牌不容易受到跨站点请求伪造(CSRF)攻击

隐式授权流不会颁发刷新令牌,主要是出于安全原因。 刷新令牌的范围不如访问令牌有限,因此赋予了更多的权限,如果泄露,将造成更大的损害。 在隐式流中,令牌在 URL 中传递,因此拦截的风险高于授权码授予流程。

但是,JavaScript 应用程序有另一种机制可用于续订访问令牌,而无需反复提示用户输入凭据。 应用程序可以使用隐藏的 iframe 针对 Azure AD 的授权终结点执行新的令牌请求:只要浏览器对 Azure AD 域仍具有活动会话(读取:具有会话 Cookie),身份验证请求就可以成功发生,而无需用户交互。

此模型授予 JavaScript 应用程序独立续订访问令牌的能力,甚至可以获取新 API 的新令牌(前提是用户以前同意了这些令牌)。 这可以避免增加获取、维护和保护高价值项目(如刷新令牌)的负担。 实现无提示续订的机制(Azure AD 会话 Cookie)在应用程序外部管理。 此方法的另一个优点是,用户可以使用登录到 Azure AD 的任何应用程序(在任何浏览器选项卡中运行)从 Azure AD 注销。 这会导致删除 Azure AD 会话 Cookie,JavaScript 应用程序将自动失去续订已注销用户的令牌的能力。

隐式授权是否适合我的应用?

隐式授权相比其他授权方式存在更多风险,需特别关注的领域已有详细记录(例如,在隐式流中滥用访问令牌来冒充资源所有者OAuth 2.0 威胁模型和安全注意事项)。 但是,较高的风险概况主要是因为它旨在启用由远程资源通过浏览器执行活动代码的应用程序。 如果计划 SPA 体系结构,则无需后端组件或打算通过 JavaScript 调用 Web API,建议使用隐式流进行令牌获取。

如果应用程序是原生客户端,则隐式流并不合适。 在本地客户端背景下,缺少 Azure AD 会话 Cookie 会剥夺应用程序保持长期会话的能力。 这意味着,在获取新资源的访问令牌时,应用程序会反复提示用户。

如果要开发包含后端的 Web 应用程序,并从其后端代码使用 API,则隐式流也不适合。 其他赠款给你更多的权力。 例如,OAuth2 客户端凭据授予提供了获取令牌的功能,这些令牌反映了分配给应用程序本身的权限,而不是用户委派。 这意味着,即使用户未主动参与会话,客户端也能保持对资源的编程访问,等等。 不仅如此,而且此类赠款提供了更高的安全保证。 例如,访问令牌永远不会通过用户浏览器传输,它们不会冒险保存在浏览器历史记录中,等等。 客户端应用程序还可以在请求令牌时执行强身份验证。

后续步骤