Sign-up/Sign-in 在 SPA 和 REST 之间 API 使用 OpenID Connect

Sign-up/Sign-in between SPA and a REST API with OpenID Connect

考虑:

  1. SPA 或静态生成的 JAMStack 网站(用 Vue/Nuxt 编写)。
  2. 云中的 REST API(Node + Express)运行。

该网站正在通过 CDN 提供服务。

为了方便用户,我们需要添加社交登录功能。 API 已经使用基于凭证的 JWT (access/refresh) 令牌流对用户进行身份验证。除此之外还有社交登录。

我们选择了授权代码流作为这个实现的机制(而不是PKCE,因为我们希望SPA使用API 的访问令牌,而不是授权提供商的访问令牌)。


我们的流程如下:

  1. 用户单击按钮并被定向到身份提供者(在我们的例子中,Google)的网站以证明其身份。我们提供随机生成的 state 令牌作为请求参数,以防止 CSRF 攻击。

  2. 在 IdP 的身份验证页面上,用户提交他们的凭据,如果成功,用户将被重定向回我们的网站(SPA),并带有状态令牌和 代码 作为 url 参数。

  3. 然后我们将状态值与我们在应用程序上生成的状态值进行匹配以抵御 CSRF 攻击。

  4. 我们在 AJAX 请求中将 code 值发送给我们的 API。

  5. API 使用此 code 和我们的 client secret 并将它们交换为已获取的身份与身份提供者(忽略访问令牌,因为我们只需要身份证明)。

  6. 如果验证了用户身份,API 生成一对访问和刷新令牌并将它们发送到 SPA。 (如果我们的数据库中不存在该用户,则会添加该用户并类似地为我们的 API 返回令牌)。 注意:我们根本不会在服务器上存储从 IdP 收到的身份令牌。

  7. SPA 收到响应后,将访问令牌存储在本地存储中(刷新令牌由 API 存储在仅 HTTP cookie 中)。

  8. 一旦刷新令牌和访问令牌都过期(通过注销),SPA 对我们 API 的所有进一步请求都是使用我们的 API 颁发的访问令牌发出的或超时),用户必须从步骤 1 重新启动身份验证流程。


我有两个问题:

  1. 假设 IdP 和 SPA、SPA 和 API、API 和 IdP 之间的所有通信都通过 TLS 进行,是否存在任何安全漏洞出现在我们的过程中?

  2. 这个更让我困惑,你如何持久化我们在页面重定向之间对身份验证服务器的初始请求期间生成的 state 令牌? (从我们离开我们的 SPA 在 IdP 的网页上进行身份验证到 IdP 将我们重定向回我们的 SPA)。 localStorage 是否足够安全?如果没有,sessionStorage 就足够了吗?

对于一个新项目,我会考虑使用 BFF 模式来避免在 SPA/browser 中处理令牌。因为在浏览器中没有 deal/store 令牌的安全方法。

请查看这些资源以详细了解为什么建议将此模式用于新项目: