Keycloak身份验证:外部用户如何在不暴露客户端密钥的情况下获取令牌

6
我有一个关于keycloak如何与没有GUI访问权限的客户端一起工作的问题。
基本上我有:
- 配置了领域、客户端(访问类型为“机密”)和用户的keycloak服务器。 - 具有GUI的服务器应用程序,也提供API,并使用keycloak进行安全保护(客户端、用户等)。
这已经可以工作了,因为我能够在GUI上登录,进行重定向等操作。
甚至访问API也很好用,当我可以访问GUI时:我登录我的UI,按照重定向并让我的UI显示token。 然后人类(区别于应用程序的用户)可以在任何API客户端中使用该token。
在这种情况下,用户从未看到客户端秘密,这本能地是正确的方法。(请注意,我非常愿意听取别人告诉我我的直觉是错误的!)
但迄今为止我无法找到服务器应用程序(没有GUI)可以获取有效令牌的方式?
据我所理解,授权终点需要客户端ID和客户端秘密才能获得令牌,这是我宁愿避免的:我不认为将我的客户端秘密提供给所有“客户”是正确的方式。
或者,我可以在我的客户端上创建一个API,要求用户凭据并代表其请求令牌,但这会将客户端凭据暴露给我的应用程序,这违反了整个概念!
我尝试将客户端访问类型设置为公共,但是当我使用下面的API调用时,也会发生错误:
POST /auth/realms/realmname/protocol/openid-connect/tokenAPI 

'grant_type=client_credentials'
'client_id=client_id'
'username=username'
'password=password'

{
    "error": "unauthorized_client",
    "error_description": "Public client not allowed to retrieve service account"
}

有人知道这应该怎么做吗?
提前感谢。
Max

你好,如果我的回答符合你的需求,能否“接受”它呢?(参考:https://meta.stackexchange.com/questions/5234/how-does-accepting-an-answer-work)。如果不行,请让我知道缺少什么。这将有助于我们和未来的访问者。 - bsaverino
2个回答

4

(...)一个没有GUI的服务器应用可以使用客户端凭据流程获得有效的令牌。

但在这种情况下,我们将为您的服务器(客户端?)应用程序定义一个专用客户端进行身份验证。返回的令牌(不绑定特定用户)将用于授权允许的应用程序(即您的经典GUI或API客户端)。

因此,基本上您应该(非常简短地):

  • 在Keycloak中定义一个特定的机密客户端
  • 将所需的应用程序(或其他客户端)添加到客户端作用域中。您希望从此客户端转移授权的客户端。
  • 使用客户端凭据流程对此客户端进行身份验证(给出令牌端点、客户端ID、凭据、作用域)
  • 确保通过TLS进行身份验证,并将参数包含在请求正文中(而不是标头-以增强隐私)
  • 进一步加强客户端的安全性

当您不再希望此特定服务器(客户端?)应用程序访问您的应用程序时,您可以更改相应的“身份验证”客户端的密钥/凭据或仅删除它。


“我认为把我的客户机密给所有客户并不是正确的做法。”

您是正确的,上述提议方法严格避免了这种情况。每个客户都将拥有自己的凭据。

编辑

(添加更多细节)

通过执行上述步骤,您将获得以下方案:

                                   Flow         Keycloak Server
C/S app. or Customer X  <--- Client Creds --->  Auth. Client X
                         --- Access Token --->  Appl. Client    <-->  Appl. Server

C/S app. or Customer Y  <--- Client Creds --->  Auth. Client Y
                         --- Access Token --->  Appl. Client    <-->  Appl. Server

         Browser users  <--- Standard  ------>  Appl. Client    <-->  Appl. Server

注意:这不是一份详细的流程图。箭头主要展示关系。

最后请注意,术语可能会有所不同,但所提出的方法基本与Google使用的相同。因此,您可以从中获得一些启发:


1
非常感谢@bsaverino,这似乎回答了我的问题。不过我希望有更简单的方法...是否有一种方式,让消费应用程序直接向Keycloak请求一个令牌(令牌1),而不需要提及客户端。然后应用程序将调用客户端上的API并提供token1。客户端将使用其id / secret + token1调用keycloak,接收最终的访问令牌并将其返回给请求者? - maxime chevry
据我所知,适用于M2M场景的所有OAuth2流程(其中用户和社交场景没有意义)都需要指定客户端ID。就我所理解的你想要做的事情,看起来非常混合,我宁愿选择经典的授权代码流程(使用用户名/密码,即https://auth0.com/docs/flows/authorization-code-flow),并在您的应用程序中实现一种方法来返回用户访问令牌(通常应保持隐藏以避免“令牌泄漏”……)。 - bsaverino
我不建议使用这种技术,因为返回的令牌可能会暴露比必要范围或声明更多的内容,或者可能过于宽松、生命周期过长或针对公共/外部使用配置错误。从某种意义上说,这基本上等同于隐式流程,由于一些原因(实际上还有更多原因),该流程已被弃用或不再推荐使用。 - bsaverino

2
我几周前也遇到了同样的问题。我的情况是我有一个后端API和一个前端应用程序供用户使用。但是,我无法将client_secret共享给前端应用程序。因此,这是我的解决方案:
1. 在keycloak上创建一个名为front_end_client的客户端,授权类型为public。此客户端将由前端应用程序使用,使用implicit flow(使用PKCE会更安全)来验证用户身份。 2. 在keycloak上创建第二个客户端(在与第一个客户端相同的REALM中),授权类型为confidential。此客户端将由后端API使用。
现在,它是如何工作的:
1. 前端应用程序验证用户并获取访问令牌(使用front_end_client)。 2. 前端应用程序将此令牌发送到后端以进行每个请求。 3. 后端应用程序验证此令牌,并可以从中检索权限。

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