Oauth2 隐式授权流和第 3 方 cookie 中的刷新令牌

Refreshing token in Oauth2 Implicit Grant Flow and 3rd party cookies

我想知道如何处理 Oauth2 隐式授权中的刷新令牌 2019 年主流浏览器默认禁用第 3 方 cookie。

一些细节:

当前设置:

场景:

问题:

问题:

我的第 3 方 cookie 问题有什么解决方案吗?

如果没有,是否有任何可用于登录和刷新令牌的隐式授权流程和 SPA 替代方案?

因为您的应用程序和身份服务器托管在不同的域中。这意味着您的应用程序正在进行跨源身份验证。跨域认证是通过第三方cookie实现的,禁用第三方cookie会导致跨域认证失败。

回答您的问题:

我的第 3 方 cookie 问题有什么解决方案吗?

Host both your application and the identity server under the same domain. You can use the subdomain in that case.

如果没有,是否有任何可用于登录和刷新令牌的隐式授权流程和 SPA 替代方案?

No

解法:

我对 CloudFoundry 不熟悉。不确定他们是否支持。您可以通过在身份提供者端启用自定义域来解决此问题。因此,您的应用程序和身份提供者都将位于同一域中,并且 cookie 将被视为第一方。例如,将您的应用程序托管在 https://acme.com and set your identity provider custom domain as https://login.acme.com

Question:

Is there any solution for my issue with 3rd party cookies?

如果您在您的应用程序和您的 IDP 之间使用相同的顶级域,那么当第 3 方 cookie 被禁用时,您应该没有问题。 This link 还详细说明了使用跨域策略的成功与否。

If not, are there any alternatives for Implicit Grant flow and SPA that I could use to sign in and refresh tokens?

我以前没有使用过 CloudFoundry,但是大多数大型 OAuth2.0 提供商都提供 public 客户端功能,其中 public 客户端(例如您的 SPA)不需要客户端密码来获取访问/刷新令牌。这允许 public 客户端使用 Authorisation Code Grant 允许通过刷新令牌刷新令牌,从而避免使用 HTTP 重定向和 cookie 的 silent auth 技术。

问题的根源在于使用 iframe 和隐式授权类型。

我认为您使用 iframe 的原因是为了跨域访问 cookie。现在,避免使用 iframe 的最简单方法是将 cookie 的域设置为 Domain=example.com,并在 example.com 上同时设置 UI 和授权服务器。如果由于某种原因,您不能这样做,您需要采用以下方法。


推荐选项

隐式授权类型不安全。虽然这个问题不是关于grant types的利弊,但是为了给我要解释的选项做背景铺垫,我简单列举一下我说Implicit flow is not secured的原因:

  1. 缺少通过提供客户端密码和授权码进行客户端身份验证的步骤。安全性较低
  2. 访问令牌作为 URL 片段发回(这样令牌就不会发送到服务器),它将继续保留在浏览器历史记录中
  3. 如果发生 XSS 攻击,恶意脚本可以很好地将令牌发送到攻击者控制的远程服务器

因此,推荐的选项是使用授权码授予类型。在 SPA(单页应用程序)中不使用授权码的原因之一是,它要求将客户端机密存储在浏览器中,我们知道浏览器无法保密。通过在服务器端使用代理组件(可以嵌入资源服务器)来保存客户端机密并充当 SPA 和授权服务器之间的代理,可以很容易地减轻这种风险。

此处(在授权代码授予类型中)流程如下所示:

  1. 用户点击 SPA 登陆页面上的登录按钮
  2. 用户被重定向到授权服务器登录页面。客户端 ID 在 URL 查询参数
  3. 中提供
  4. 用户输入他/她的凭据并单击登录按钮。用户名和密码将使用 HTTP POST 发送到授权服务器。凭据应在请求 body 或 header 中发送,而不是在 URL 中发送(因为 URL 记录在浏览器历史记录和应用程序服务器中)。此外,应设置正确的缓存 HTTP headers,以便不缓存凭据:Cache-Control:no-cache,no-store,Pragma:no-cache,过期:0

  5. 授权服务器根据用户数据库(例如 LDAP 服务器)对用户进行身份验证,其中存储了用户名和用户密码的哈希值(哈希算法,如 Argon2、PBKDF2、Bcrypt 或 Scrypt)随机加盐

  6. 身份验证成功后,授权服务器会根据 URL 查询参数中提供的客户端 ID 从其数据库中检索重定向 URL。重定向 URL 是资源服务器 URL
  7. 然后用户将被重定向到资源服务器端点,在 URL 查询参数中使用授权码
  8. 然后,资源服务器将向授权服务器发出 HTTP POST 请求以获取访问令牌。授权代码、客户端 ID、客户端密码应包含在请求 body 中。 (应使用如上所述的适当缓存 headers)
  9. 授权服务器将return访问令牌和刷新令牌作为响应body或header(如上所述使用适当的缓存header)
  10. 资源服务器现在会将用户(HTTP 响应代码 302)重定向到 SPA URL,方法是将具有域属性的适当 cookie 设置为 Domain=example.com(假设资源服务器和 UI 都在 example.com 的 sub-domains 上)。授权服务器的域无关紧要,因为它没有设置任何 cookie。

同理,访问令牌刷新请求可以发送到代理组件,代理组件会从cookie中读取刷新和访问令牌,并使用提取的令牌调用授权服务器api 、客户端 ID 和客户端密码。

最后,我们决定采用不同的解决方案。当 JWT 生命周期结束时,我们会显示一个模式通知会话已超时,并带有 2 个按钮,一个用于注销,一个用于保持会话。当用户点击“保持会话”时,将打开新的 tab/popup-window,在 IDP 中通过再次提供用户凭据或自动(如果 IDP 会话仍处于活动状态)重新验证用户。

所以流程是:

JWT lifetime ends -> 'keep session' in modal chose -> open new tab/popup-window with IDP login form -> successfully authenticated -> redirect back to app -> store token in browser's storage -> close popup-window/tab with window.close() -> get new token from storage and use it in next calls

因为我们使用新的 popup-window/tab 重新验证,所以第 3 方 cookie 没有问题。

这也提供了一个巨大的优势。用户无论何时回到应用程序都不会丢失他的工作,因为模态将在那里等待。我想,另外它让我们认识了 Re-authenticing accessibility success criterion (level AAA)

Success Criterion 2.2.5 Re-authenticating

When an authenticated session expires, the user can continue the activity without loss of data after re-authenticating.