现代浏览器中的跨站点身份验证,无需第 3 方 cookie

luc*_*nov 5 authentication safari privacy google-chrome local-storage

系统有多个部分位于不同的域(而不是子域)。在用户登录任何域后,我需要在所有域中对用户进行身份验证,而无需用户进行任何交互。

过去我在公共域中使用 cookie。现在这是不可能的 - https://developer.chrome.com/docs/privacy-sandbox/chips/

然后我们使用带有 LocalStorage 的 iframe 和 postMessage 到父窗口 - https://github.com/zendesk/cross-storage。它最近也停止工作,因为现在不同的域对于公共域有单独的 LocalStorage - https://developer.chrome.com/docs/privacy-sandbox/storage-partitioning/

Chrome 已经宣布了可能的解决方案:SharedStorage - https://developer.chrome.com/docs/privacy-sandbox/shared-storage/但是:

  • 它在 Safari 中不起作用(MacOS 和 iOS 用户对我们很重要)
  • 您必须以公司身份申请并注册您的域名(对我们来说不是问题,但对于舞台和开发环境来说不方便)

我只看到一种防弹解决方案:

  • 将所有未经授权的用户重定向到公共域
  • 公共域生成随机令牌(或从 cookie/localstorage 中获取(如果存在))并使用此令牌重定向回原始 URL
  • 域尝试使用此令牌进行身份验证
  • 如果未经过身份验证,则在登录/注册期间添加此令牌,以便下次它将自动登录用户
  • 注销期间删除此用户令牌,使其不再有效

这对于用户来说是糟糕的 UI - 2 个额外的重定向;似乎过于复杂。

也许有一个更好的解决方案可以在现代浏览器中工作?

Jul*_*dio 0

这个答案分为两部分,第一部分是关于高层设计,第二部分是关于所讨论的具体技术问题。

高层设计

系统有多个部分位于不同的域(而不是子域)。在用户登录任何域后,我需要在所有域中对用户进行身份验证,而无需用户进行任何交互。

我将谈论相对于应用程序域/站点的更抽象概念:

在理想的情况下,有多个应用程序,再加上一个专用于身份验证以及更一般地说身份服务的单独应用程序。(顺便说一句,请注意,授权,即处理可能与给定特定上下文的身份(用户)关联的特定角色和权限,实际上通常是特定于应用程序的,与身份服务无关。)

我只看到一种防弹解决方案:

  • 将所有未经授权的用户重定向到公共域

是的,尽管并不严格需要重定向:您的身份服务可能只是公开 Web 服务,并且单个应用程序代表用户在幕后与其进行交互。但重定向仍然是规范的解决方案,不仅是为了避免代码重复,而且特别是在相关的地方,以便任何注册/登录/管理帐户体验在所有应用程序中保持统一。

  • 公共域生成随机令牌(或从 cookie/localstorage 中获取(如果存在))并使用此令牌重定向回原始 URL

生成新的令牌,期间。并将其存储在服务器上以供以后使用,特别是验证(见下文)。-- 抱歉,我没有这方面的参考资料,但我会将其作为安全最佳实践,即登陆登录页面的经过身份验证的用户会自动注销(删除 cookie 和/或与登录关联的任何存储的令牌)撤销):这可以保持用户流程“干净”。

  • 域尝试使用此令牌进行身份验证

具体来说,一旦用户通过身份验证,从哪里获得他们的身份验证令牌,他们就会将其与每个请求一起提交给任何应用程序:应用程序此时必须做的是验证令牌(这是标准术语,包括解密有效负载),它再次利用身份服务公开的 Web 服务来完成此操作。

令牌通常直接在有效负载中携带身份信息:这个想法对于任何特定应用程序来说都有足够的信息,能够唯一地识别用户并在需要时进行任何授权。

这对于用户来说是糟糕的 UI - 2 个额外的重定向;似乎过于复杂。

事实上,重定向到登录页面,然后重定向回用户尝试访问的页面,是规范的解决方案,特别是如果用户明确注册/管理自己的帐户和个人资料:如果不需要,例如在Intranet 环境或机器对机器的身份验证,仅围绕专用 Web 服务可能就足够了。

技术问题

对于架构概述已经说了这么多,我应该特别提到一个技术问题,因为它明确地出现在问题中:

如果登录页面与应用程序本身位于不同的域中,那么客户端如何将成功登录时生成的令牌转发到任何其他应用程序页面,因此我们会受到跨域限制。事实上,我们可以通过多种方法来解决这个问题,我只是快速提及最常见的方法和我能想到的方法:

  • CORS 和跨域 cookie:也许是最干净、最适合我们目的的。 https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS 这可能会出现广告拦截器和类似问题,因为它们被视为第三方 cookie:另一方面,这听起来并非不合理要求用户添加与相关域相关的例外规则,只要他们确实想要登录和使用应用程序...

  • 使用 HTTP 状态代码 307 进行重定向,用于将令牌 POST 回应用程序。 https://www.rfc-editor.org/rfc/rfc9110.html#name-307-temporary-redirect https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/307 我有我自己没有直接的经验,但是人们,例如这里的SO,说浏览器在允许重定向之前要求用户确认,尽管我在官方文档中找不到任何对此要求的引用[添加:无论如何,它是允许的语句“用户代理可以使用位置字段值进行自动重定向”(我的重点),因此重定向可能不是自动的]:但是,如果大多数浏览器确实如此,那么这很可能不是一个在我们的场景中,这是一个很好的解决方案,但它会无缘无故地导致糟糕的用户体验。

  • 返回一个 HTML 页面,其中包含包含令牌的隐藏字段,然后在页面加载时由 JS 自动提交。我认为这也是一个可以接受的解决方案,就用户体验而言,实际上它与通常的客户端重定向没有太大不同,页面显示“在 3,2,1... 秒内自动重定向:单击此如果不起作用,请链接”。潜在的缺点是,这在禁用 JS 的情况下不起作用,另一方面,如上所述,在禁用 JS 的情况下浏览网页的用户肯定不会不习惯单击链接或按下提交按钮才能继续。

  • 在iframe中打开登录页面并使用window.postMessageAPI​​跨域交换结果令牌。 https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage 这个需要更多的客户端编码,它也具有前面提到的所有基于 JS 的解决方案的局限性,但是,在哪里期望用户启用 JS 是合理的,这也是一个可行的解决方案,根据我的经验,效果很好。

  • 共享本地存储和类似的实验功能https://developer.chrome.com/docs/privacy-sandbox/shared-storage/ 我认为浏览器尚未广泛支持的任何“特殊”功能在这里都不可行:例外是以下上下文:对所使用的浏览器有完全的控制权,但我认为这种情况正在消失:在我们普遍访问和随时随地工作的时代。