Keycloak:为错误的客户模拟铸造令牌

Posted

技术标签:

【中文标题】Keycloak:为错误的客户模拟铸造令牌【英文标题】:Keycloak: Impersonation mints token for wrong client 【发布时间】:2020-10-09 11:50:21 【问题描述】:

我正在尝试通过使用信任客户端并为公共客户端颁发令牌来通过模拟来颁发访问令牌。我已经设置了令牌交换权限并且请求有效。但是,我的问题是颁发的令牌似乎在 AZP 中包含错误的客户端。

以下是我的要求:

curl -v -X POST \
    -d "client_id=impersonator-client" \
    -d "client_secret=<secret omitted>" \
    --data-urlencode "grant_type=urn:ietf:params:oauth:grant-type:token-exchange" \
    -d "audience=target-client" \
    --data-urlencode "requested_subject=john.doe" \
    http://localhost:8080/auth/realms/swarm/protocol/openid-connect/token

基本上,我想通过使用“impersonator-client”模拟用户来获取用户“john.doe”的访问令牌。颁发的令牌应该是为“target-client”铸造的,但是 AZP 仍然包含“impersonator-client”。

我这样做的原因是因为应该可以使用外部身份验证工作流程登录,最终提供一个访问令牌,然后可以在我的后端服务器上进行验证,该服务器使用受信任的客户端冒充。

根据文档 (https://www.keycloak.org/docs/latest/securing_apps/#_token-exchange),受众应该定义应该为其铸造令牌的目标客户端。

关于权限:

我已经使用引用“impersonator-client”的客户端策略设置了“admin-impersonating.permission.users”权限。 “target-client”本身配置有权限“token-exchange.permission.client.e236d39c-9b9c-4815-b734-90364fea4e91”,其中包括引用“impersonator-client”的客户端策略。我在那里遗漏了什么吗?这里的问题是文档似乎是错误的。文档使用“user-impersonated.permission.users”而不是“admin-impersonating.permission.users”。当我这样尝试时,请求甚至被拒绝了。

这是 Keycloak 中的错误还是我做错了什么?

提前致谢!

【问题讨论】:

您找到解决方案了吗? 【参考方案1】:

好的,我遇到了这个问题,最大的问题之一是它阻止了我的应用程序(target-client)执行刷新令牌工作流。如果我使用预期的目标客户端执行 refresh_token 授权,我会收到一条错误消息 Session doesn't have the required grant. 如果我使用启动客户端(与令牌中的 azp 字段匹配),我会收到 Invalid refresh token. Token client and authorized client don't match

我发现了这个issue,它把我带到了this,当要求进行令牌交换时,它又指定了starting-clientclient_id 是导致错误azp 访问和刷新令牌和混乱的原因一切都好。我不知道这是不是故意的,文档是错误的,或者这是一个错误,我将来会遇到问题。我会在以后查看这些错误报告以了解更多详细信息。

所以这是进行令牌交换的工作方式(我使用的是 Keycloak 15.0.2),然后从中刷新。这是 Kotlin,但你明白了。这假设您已经执行了必要的配置here。

// tokenManager makes sure I have a valid access token from "starting-client"
// via client_credentials grant. You can swap with however you want to get this token.
val adminToken = keycloak.tokenManager().accessTokenString

// exchange your admin token for starting-client for a user token at target-client
val response = Unirest.post("$keycloakProperties.authServerUrl/realms/$keycloakProperties.realm/protocol/openid-connect/token")
            .field("grant_type", "urn:ietf:params:oauth:grant-type:token-exchange") // using the keycloak custom token-exchange grant
            .field("client_id", "target-client") // set to the target client ID, not the starting as the docs say!
            .field("subject_token", adminToken) // seems the exchange grant reads the starting-client from the subject token, so setting "client_id" to "starting-client" (which is what I had been doing) forces the exchanged token into an invalid state
            .field("requested_token_type", "urn:ietf:params:oauth:token-type:refresh_token") // refresh_token will issue you both an access and refresh token
            .field("audience", "target-client")
            .field("requested_subject", userId) // the keycloak user ID we are requesting a token for
            .asObject(AccessTokenResponse::class.java) // this is just some DTO so I can deserialize the JSON into an object for easy use, I happened to have this from the Keycloak admin client laying around
            .body

// we have our access token now. let's refresh it for the sake of the example, but normally your app would do this periodically on the client end.
val refreshedToken = Unirest.post("$keycloakProperties.authServerUrl/realms/$keycloakProperties.realm/protocol/openid-connect/token")
            .field("grant_type", "refresh_token")
            .field("client_id", "target-client")
            .field("refresh_token", accessToken.refreshToken)
            .asObject(AccessTokenResponse::class.java)
            .body

// voila, refreshedToken has given you a new access and refresh token
log.debug("New access token , refresh token ", refreshedToken.accessToken, refreshedToken.refreshToken)

【讨论】:

以上是关于Keycloak:为错误的客户模拟铸造令牌的主要内容,如果未能解决你的问题,请参考以下文章

错误无法将代码转换为令牌

Keycloak:我可以为每个客户端/用户/角色设置令牌的到期时间吗?

撤销 Keycloak 访问令牌

无法从 Keycloak 获取 oauth 令牌

Keycloak 客户端映射器将领域名称显示为 JWT 令牌中的单个属性

Keycloak 客户凭证流程说明