使用外部 OAuth 2 授权代码保护 REST API 和单页应用程序

信息安全 验证 oauth 休息 单页应用
2021-08-21 16:57:14

我试图了解如何在同时拥有单页 JS 应用程序和 REST API 时实现 OAuth 2 授权代码流。目的是通过将身份验证卸载到 OAuth 提供程序来保护对 REST API 的访问。

目前我的流程看起来像:

1)

+--------------------+               +----------------+
| JS Single Page App | - redirect -> | OAuth Provider | - user enters credentials
+--------------------+               +----------------+

2)

+----------------+                                   +----------+
| OAuth Provider | - redirect with temporary code -> | REST API |
+----------------+                                   +----------+

3)

+----------+                                      +----------------+
| REST API | - request access token using code -> | OAuth Provider |
+----------+ <- return access token ------------- +----------------+

我现在应该怎么办?我目前的理解是,我应该在收到访问令牌后将用户重定向到将再次加载 JS 单页应用程序的页面。但是我应该与单页应用程序共享访问令牌并使用它的存在来验证任何访问我的 REST API 的请求,还是更好地创建一个单独的标识符并维护两者之间的服务器端映射new-identifier->access_token无论哪种方式,将任何标识符传输给客户端的最佳方式是什么?我宁愿不维护任何会话,也不希望标识符出现在重定向页面的 URL 栏中。我目前唯一能想到的就是创建一个临时 cookie,单页应用程序会读取然后擦除它,但不知何故感觉有点笨拙。

2个回答

首先,非常清楚的是,OAuth 2 不是身份验证协议。如果您想知道用户的身份,还有其他旨在解决此问题的协议,例如OpenID Connect如果您打算使用 OAuth 2 来授权对受保护资源的访问,请继续阅读。

要回答您的直接问题:您似乎正在使用授权代码授予流程:

 +----------+
 | Resource |
 |   Owner  |
 |          |
 +----------+
      ^
      |
     (B)
 +----|-----+          Client Identifier      +---------------+
 |         -+----(A)-- & Redirection URI ---->|               |
 |  User-   |                                 | Authorization |
 |  Agent  -+----(B)-- User authenticates --->|     Server    |
 |          |                                 |               |
 |         -+----(C)-- Authorization Code ---<|               |
 +-|----|---+                                 +---------------+
   |    |                                         ^      v
  (A)  (C)                                        |      |
   |    |                                         |      |
   ^    v                                         |      |
 +---------+                                      |      |
 |         |>---(D)-- Authorization Code ---------'      |
 |  Client |          & Redirection URI                  |
 |         |                                             |
 |         |<---(E)----- Access Token -------------------'
 +---------+       (w/ Optional Refresh Token)

此流程针对机密客户端进行了优化:可以对最终用户和任何可能试图访问受保护资源的第三方保密的客户端。对于在客户端浏览器中运行的 javascript 应用程序,您无法保守秘密 - 所有代码都在那里,只需使用浏览器的调试器破解它就会揭示所有秘密。

取而代之的是隐式授予流程,专门针对像您这样的客户进行了优化。RFC 甚至使用在浏览器中运行的 JavaScript 作为打算使用此流程的客户端示例。

 +----------+
 | Resource |
 |  Owner   |
 |          |
 +----------+
      ^
      |
     (B)
 +----|-----+          Client Identifier     +---------------+
 |         -+----(A)-- & Redirection URI --->|               |
 |  User-   |                                | Authorization |
 |  Agent  -|----(B)-- User authenticates -->|     Server    |
 |          |                                |               |
 |          |<---(C)--- Redirection URI ----<|               |
 |          |          with Access Token     +---------------+
 |          |            in Fragment
 |          |                                +---------------+
 |          |----(D)--- Redirection URI ---->|   Web-Hosted  |
 |          |          without Fragment      |     Client    |
 |          |                                |    Resource   |
 |     (F)  |<---(E)------- Script ---------<|               |
 |          |                                +---------------+
 +-|--------+
   |    |
  (A)  (G) Access Token
   |    |
   ^    v
 +---------+
 |         |
 |  Client |
 |         |
 +---------+

您应该阅读 RFC 以了解有关此流程如何工作的详细信息,但流程的一般概念是不再需要使用授权代码;而是直接为客户提供访问代码。

这带来了一个缺点,即当代码不再有效时,客户端必须再次请求资源 - 无法“记住”无法保密的客户端的授权授予。这是 OAuth 作者为提高此类客户端的安全性而有意做出的决定。

我不确定我是否理解您的情况正确。如果您只想使用 OAuth 保护来自 javascript 客户端的 RESTful API 调用,那么 Tragedian 关于隐式授权类型的回答是绝对正确的。这意味着您有明确的 UI 和数据分离:

  • 无需任何形式的认证/授权即可加载 UI(即 JS 单页应用程序)
  • 在授权服务器授权之后,数据(即 RESTful API 调用)由访问令牌保护

如果您有不同的场景,请更准确地描述它。您提到了应用程序的后端部分,它也依赖于 OAuth 并且能够存储敏感数据。这意味着除了 JS 单页应用程序之外,您还有一个不同的客户端(使用 OAuth 语言)......