使用 OAuth 访问 SPA 和 REST API 时,应该在哪里为 RBAC 存储用户组成员身份

Posted

技术标签:

【中文标题】使用 OAuth 访问 SPA 和 REST API 时,应该在哪里为 RBAC 存储用户组成员身份【英文标题】:Where should be user group membership stored for RBAC when using OAuth to access SPA and REST API 【发布时间】:2021-05-03 18:33:49 【问题描述】:

我正在努力实施 oauth2 以保护将调用 REST API 并允许其他潜在客户访问相同的 REST API 的 Web 应用程序。我想使用基于角色的访问来控制从 API 返回的数据。 我将使用 Keycloak 作为授权服务器以及用户/组管理。

用例是这样的

    我将创建带有公共客户端 (SPA) 和机密的可能仅承载客户端 (REST API) 的 keycloak 领域,以及将成为这些组一部分的组和用户 用户将通过授权流程登录 SPA 并收到访问令牌。 SPA 将向 REST 服务发出请求 (XHR),将令牌作为不记名令牌传递,并根据用户所在的组检索数据或执行允许的操作。

我正在尝试了解/最佳实践我应该将用户所属的组列表存储在哪里。 SPA 和/或 REST 服务可以检索和传递的访问令牌还是 ID 令牌必须使用访问令牌和 userinfo 端点从授权服务器检索该数据。似乎 keycloak 将 JWT 用于访问和 ID 令牌,并且角色/组可以包含在两者中。我阅读了混合建议,即 REST 服务不应读取访问令牌,仅用于证明用户已通过身份验证,但后来我看到它用于传递用户组。

我的另一个问题是,如果我想允许自动化客户端访问无法使用授权流的 REST API,最佳实践是使用客户端流并在 keycloak 中加载该客户端并提供客户端 clientId 和 secret 以便能够检索访问令牌,使用它对 REST 服务进行身份验证(承载身份验证标头)

更新

我还有几个后续问题希望大家弄清楚。

至于 ID 令牌,我认为 ID 令牌仅应由正在验证用户的应用程序 (SPA) 使用,并将获取有关用户的信息(用户名、电子邮件和其他一些东西)关于声明和用户批准权限。可能会在应用程序中显示这些内容。 ID 令牌不应(永远)发送到 REST API 以检索数据。

另一方面,访问令牌不应该被应用程序 (SPA) 读取,而是在对 API 服务器 (Bearer $AUTH_TOKEN) 的每个请求中使用,API 服务器验证令牌,然后检索用户的组信息并返回允许的响应。

尚不清楚的是,如果应用程序收到授权令牌并不意味着用户已通过身份验证。为什么我们需要 ID Token。

另外,如果访问令牌并不总是携带信息并且可能只是一个随机字符串,那么您将如何知道用户的权限。我在读到有两种类型的令牌“标识符类型”和“自包含类型”。我猜如果令牌是标识符类型,那么 REST 服务将不得不向授权服务器发送请求,以通过追溯 api 获取该信息(组/权限)。

找到两篇关于这个的好文章:https://darutk.medium.com/oauth-access-token-implementation-30c2e8b90ff0.https://darutk.medium.com/api-protection-by-id-token-3123481e96f2

【问题讨论】:

【参考方案1】:

Keycloak 提供 Open ID Connect (OIDC) 标准。 OIDC 基于 OAuth 2.0。仅当您使用 OIDC(您的身份验证请求中的openid 范围)时,您才会获得 ID 令牌。 ID 令牌应用于身份验证。我会说身份验证不需要了解有关用户组的任何信息。组/角色仅对授权很重要,这是 OAuth 的任务 - 访问令牌。所以我会在访问令牌中保留组/角色。

(不幸的是)现实世界已经有应用程序,它们不遵循这个约定(嗨 Concourse :-))。他们期待 ID 令牌中的组/角色,您无法对其进行配置。这就是为什么 Keycloak 支持访问令牌和 ID 令牌中的声明的一个很好的理由。

推荐博文:https://medium.com/@nilasini/id-token-vs-access-token-17e7dd622084

OAuth 为机器到机器的应用程序提供客户端凭据流,因此它是自动化客户端的正确选择,例如 cron/CI 脚本、第 3 方应用程序访问等,您不需要用户身份。

【讨论】:

感谢您的说明。那里有很多不同的信息和很多首字母缩略词。如果您愿意,我已经更新了一些后续问题。【参考方案2】:

您应该始终将授权信息放在访问令牌中,而不是 ID 令牌中。有关用户组的信息可以出现在 ID 令牌中,例如,如果您在客户端中需要它们 - 也许您想根据用户组对某些信息进行颜色编码,等等。

我的另一个问题是,如果我想允许自动化客户端访问无法使用授权流的 REST API,最佳实践是使用客户端流并在 keycloak 中加载该客户端并提供客户端 clientId 和 secret 以便能够检索访问令牌,使用它对 REST 服务进行身份验证(承载身份验证标头)

正如 Jan 所指出的,客户端凭据流正是为此目的而创建的 - 以便后端可以相互通信。它是一种对客户端进行身份验证的方法。

尚不清楚的是,如果应用程序收到授权令牌并不意味着用户已通过身份验证。为什么我们需要 ID Token。

正如您所写的那样 - 访问令牌用于 API,ID 令牌用于客户端。当然,当您的客户端获得访问令牌时,这意味着该用户已通过身份验证,但是许多客户端希望获得有关已通过身份验证的用户的其他信息,因此他们可以例如显示您的个人资料图片、您的用户名、知道您的电子邮件等。因此,ID 令牌 - ID 令牌为您提供有关经过身份验证的用户的信息,不仅证明他们已通过身份验证。如果您只需要证明用户已通过身份验证,那么访问令牌就足够了。

另外,如果访问令牌并不总是携带信息并且可能只是一个随机字符串,那么您将如何知道用户的权限。我在读到有两种类型的令牌“标识符类型”和“自包含类型”。我猜如果令牌是标识符类型,那么 REST 服务将不得不向授权服务器发送请求,以通过追溯 api 获取该信息(组/权限)。

确切地说,如果您使用不透明字符串作为访问令牌,那么 API 将不得不向授权服务器执行自省流程(RFC 7662 中定义的标准)以验证令牌并获取相关的授权信息(例如用户组等)。如果您担心您的服务和授权服务器之间存在大量流量,您可以查看一个名为 Phantom Token flow 的模式,它利用 API 网关及其缓存来执行自省,而不是服务本身。我建议使用这种方法,尤其是当您说您将有 3d 方客户端访问您的 API 时。最好不要与 3d 方集成商共享 JWT 访问令牌。

也可以看看这篇关于API Security Best Practices的文章,它可能会对这些东西有更多的了解。

【讨论】:

以上是关于使用 OAuth 访问 SPA 和 REST API 时,应该在哪里为 RBAC 存储用户组成员身份的主要内容,如果未能解决你的问题,请参考以下文章

SPA 和 Spring Boot Rest Api 应用程序中具有授权代码授权类型的 OAuth2 流

SPA 应该在哪里保存 OAuth 2.0 访问令牌?

SPA 如何在 OAuth2 隐式流中提取访问令牌

OAuth2.0的定义

带有 spring-security 的 OAuth2 - 通过 HTTP 方法限制 REST 访问

SPA中令牌存储和刷新的选项