Keycloak公共客户端和授权

30
我们正在使用keycloak-adapter与Jetty进行身份验证和授权,使用Keycloak。根据Keycloak doc for OIDC Auth flow
另一个重要的方面是此流程中公共客户端与机密客户端的概念。当机密客户端交换临时代码以获取令牌时,需要提供客户端密钥。公共客户端不需要提供此客户端密钥。只要严格执行HTTPS并且对客户端注册的重定向URI非常严格,公共客户端就非常好。
HTML5 / JavaScript客户端始终必须是公共客户端,因为没有一种安全传输客户端密钥的方式。
我们有连接到Jetty并使用auth的Web应用程序。因此,我们创建了一个公共客户端,并且对于Web应用程序/ REST身份验证,它运行得非常好。问题在于,一旦启用授权,客户端类型从公共转换为机密,并且不允许将其重置为公共。现在,我们很困扰。由于授权,我们无法拥有公共客户端,也无法将Web应用程序连接到机密客户端。
这对我们来说似乎是矛盾的。您知道为什么客户端需要保密才能进行授权吗?对于如何解决此问题,您能提供任何帮助吗?谢谢。

这里有什么指针吗? - NumeroUno
你使用的是哪种OAuth流程?是授权码授予、隐式授权还是其他什么流程? - RrR-
基于用户名/密码和JWT令牌的授权流程是正常的。 - NumeroUno
认证码和隐式流程都基于这个概念,但是为不同的目的而创建。 - RrR-
我认为作为资源服务器的Jetty应用应该是私有的。你的前端客户端应该是公开的,因为有人可以使用客户端并在开发者控制台中看到客户端密钥。 在任何人能够帮助你之前,我认为你的问题需要更多关于架构和问题本身的信息。 - Thomas Lann
4个回答

32
据我的理解,您已经将前端和后端应用程序分开。如果您的前端是一个静态 Web 应用程序,并且没有由相同的后端应用程序(服务器)提供服务,而您的后端是一个简单的 REST API,则会配置两个 Keycloak 客户端:

  • public 客户端用于前端应用程序。它将负责获取 JWT 令牌。
  • bearer-only 客户端,将附加到您的后端应用程序中。

为了启用授权,您需要创建角色(可以是领域或客户端范围的,从领域级别开始,因为它更容易理解)。然后,每个用户都将在 Keycloak 管理 UI 中分配一个或多个角色。基于此,您应该配置您的 Keycloak 适配器配置(在后端上)。

综上所述,为了与您的 REST API 进行通信,您需要将 JWT 令牌附加到每个 HTTP 请求的授权标头中。根据您的前端框架,您可以使用以下任一方法:

P.S. 为了调试,我刚刚编写了一个名为 brauzie 的 CLI 工具,它将帮助您获取和分析 JWT 令牌(作用域、角色等)。它可以用于公共和机密客户端。您也可以使用Postmanhttps://jwt.io

希望对您有所帮助 :)


当我注销(从前端应用程序或使用keycloak管理员控制台关闭会话)之后,我的后端REST API仍然需要工作吗?我在keycloak中有一个公共客户端,并且我的后端应用程序已经设置为仅承载。 - Hector
3
你能详细说明授权应该如何配置吗?如果REST API使用从客户端传递的JWT令牌来评估资源访问权限,并且keycloak会评估JWT的azp(issued-for)字段,那么我会收到Client application [public-client] is not registered as a resource server.的错误提示,因为 keycloak 无法识别它作为资源服务器。https://github.com/keycloak/keycloak/blob/11.0.3/services/src/main/java/org/keycloak/authorization/protection/ProtectionService.java#L133。 - lordvlad

3
客户需要保密以进行授权的原因是什么呢?
客户需要通过Keycloak进行身份验证,以充分利用保护API、管理资源和创建PAT令牌。
Web应用程序应该有自己的客户端,禁用身份验证,并使用适合单页应用程序的正确身份验证流程(带有PKCE的授权码流)。
发给前端客户端的访问令牌实际上可以与代表身份验证服务器的不同后端客户端一起使用。一个示例请求在(尽管简洁)Keycloak文档中有记录这里
curl -X POST \
  http://${host}:${port}/realms/${realm}/protocol/openid-connect/token \
  -H "Authorization: Bearer ${access_token}" \
  --data "grant_type=urn:ietf:params:oauth:grant-type:uma-ticket" \
  --data "audience={resource_server_client_id}" \
  --data "permission=Resource A#Scope A" \
  --data "permission=Resource B#Scope B"

上面的例子展示了使用访问令牌(可能由前端客户端用于azp)和后端客户端ID作为受众的情况。这样可以解决上述评论中提到的错误。请参考@NumeroUno's answer
Client application [public-client] is not registered as a resource server. 

1
这是正确的答案。Keycloak是很多东西,而细粒度的保护并不符合我对认证的常见理解。我强烈建议阅读整个文档https://www.keycloak.org/docs/latest/authorization_services/index.html#_overview,它会澄清一些细节。 - undefined

2
经过深思熟虑,我们发现在连接到公共客户端时不需要启用授权。当任何请求发送到公共客户端时,它只进行身份验证部分。授权部分是在实际请求落在资源服务器上(在我们的案例中是Jetty)使用机密客户端完成的(因为Jetty已经配置了机密客户端)。

4
我还不理解。机密客户端仍需要身份验证。如果您只在公共客户端上进行身份验证,那么如何将令牌“传递”给机密客户端?或者您需要再次进行身份验证? - monzonj
1
@NumeroUno,您能否提供更多细节,以便说明它们如何连接? - Amit Dugar
2
@NumeroUno 有没有可能详细解释一下?我试图传递公共客户端接收到的访问令牌以获取对资源的访问权限,但由于保护服务评估JWT令牌的azp(发行方)字段,它尝试在公共客户端上查找资源,而该客户端没有定义任何资源:客户端应用程序[public-client]未注册为资源服务器。 - lordvlad
2
好的,这与authzClient.protection(userJwt)等效。现在,如果您使用公共客户端进行授权,则userJwt中的azp声明将指向公共客户端。如果我从公共客户端传递该userJwt到机密客户端,并让机密客户端向keycloak请求uma令牌,则会失败,因为该令牌是为公共客户端颁发的,而公共客户端没有配置授权。在您的情况下,如何将上下文从公共客户端切换到机密客户端? - lordvlad
1
@Harvey,我尝试了token exchange,并且已经使其正常工作。后端使用公共客户端令牌交换私有客户端令牌,并使用新的私有客户端令牌执行授权查找。最终,我选择在我们的应用程序内进行授权,因为与Keycloak的额外往返对我们应用程序的响应时间产生了非常不良的影响。 - lordvlad
显示剩余14条评论

2
我认为您指的是在创建客户端时Keycloak管理控制台中的“启用授权”开关。 如果您在标签旁边单击问号,您将看到提示:“为客户端启用/禁用细粒度授权支持。” 在Keycloak管理控制台中创建客户端(v 6.0.1) 这适用于为充当资源服务器的后端应用程序创建客户端的情况。 在这种情况下,客户端将是机密的。
如果要为前端应用程序创建客户端以对用户进行身份验证并获取JWT,则不需要此选项。
另请参阅:https://www.keycloak.org/docs/latest/authorization_services/index.html

1
是的,我需要这个。我需要Keycloak来授权我的SPA(公共客户端)的请求,这样我就不需要在所有资源服务器中重复实现逻辑。我不明白为什么他们不为公共客户端启用授权。 - hirikarate

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接