如何在C#应用中通过API为Keycloak的access_token设置自定义Claim
解决Keycloak生成带自定义Claim的Access Token问题
需求背景
同一用户需根据应用状态访问不同资源,需在C#应用中生成带自定义Claim的Keycloak Access Token,排除创建多用户、Cookie存储、URL传参的实现方案。尝试使用uma-ticket方式时出现错误:
{
"error": "invalid_grant",
"error_description": "Invalid bearer token"
}
可行实现方案
方案1:基于资源所有者密码凭证流扩展自定义Claim
Keycloak后台配置自定义Claim映射
- 进入目标Realm的客户端页面,选择你的应用客户端,切换到Mappers标签
- 点击Create,选择
Script Mapper类型 - 填写名称后,在Script中编写逻辑,根据请求传入的自定义参数生成Claim,示例脚本:
var customParam = session.getContext().getRequestParameters().getFirst("app_state"); if (customParam) { token.setOtherClaims("app_state", customParam); } - 保存映射规则,确保
Token Claim Name设置为你需要的Claim键名(比如app_state)
C#代码调用Token端点生成带自定义Claim的Token
using System.Net.Http; using System.Collections.Generic; using System.Threading.Tasks; using Newtonsoft.Json.Linq; public async Task<string> GetAccessTokenWithCustomClaim(string username, string password, string appState) { var client = new HttpClient(); var tokenEndpoint = "http://你的Keycloak地址/auth/realms/你的Realm名/protocol/openid-connect/token"; var formData = new Dictionary<string, string> { { "grant_type", "password" }, { "client_id", "你的应用客户端ID" }, { "client_secret", "你的应用客户端密钥" }, // 仅保密客户端需要填写 { "username", username }, { "password", password }, { "app_state", appState } // 对应Script Mapper中读取的自定义参数 }; var response = await client.PostAsync(tokenEndpoint, new FormUrlEncodedContent(formData)); response.EnsureSuccessStatusCode(); var responseJson = JObject.Parse(await response.Content.ReadAsStringAsync()); return responseJson["access_token"].ToString(); }
方案2:基于客户端凭证流+用户模拟生成Token(无需用户密码)
适合后台服务场景,需管理员权限:
Keycloak权限配置
- 给用于调用接口的客户端添加
realm:impersonation角色(在客户端Scope标签页添加对应角色)
- 给用于调用接口的客户端添加
C#代码实现
using System.Net.Http; using System.Collections.Generic; using System.Threading.Tasks; using Newtonsoft.Json.Linq; public async Task<string> GetImpersonatedTokenWithCustomClaim(string userId, string appState) { var client = new HttpClient(); // 1. 获取管理员级别的客户端凭证Token var adminTokenEndpoint = "http://你的Keycloak地址/auth/realms/你的Realm名/protocol/openid-connect/token"; var adminForm = new Dictionary<string, string> { { "grant_type", "client_credentials" }, { "client_id", "管理员客户端ID" }, { "client_secret", "管理员客户端密钥" } }; var adminResponse = await client.PostAsync(adminTokenEndpoint, new FormUrlEncodedContent(adminForm)); var adminToken = JObject.Parse(await adminResponse.Content.ReadAsStringAsync())["access_token"].ToString(); // 2. 调用用户模拟接口,传入自定义Claim var impersonateEndpoint = $"http://你的Keycloak地址/auth/admin/realms/你的Realm名/users/{userId}/impersonation"; var requestBody = new { clientId = "你的应用客户端ID", additionalClaims = new Dictionary<string, object> { { "app_state", appState } } }; client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", adminToken); var impersonateResponse = await client.PostAsJsonAsync(impersonateEndpoint, requestBody); impersonateResponse.EnsureSuccessStatusCode(); var impersonateJson = JObject.Parse(await impersonateResponse.Content.ReadAsStringAsync()); return impersonateJson["token"].ToString(); }
uma-ticket错误排查方向
出现invalid_grant: Invalid bearer token通常是以下原因:
- 传入的Bearer Token无效(过期、签名错误、不属于目标Realm/客户端)
- 未正确配置UMA资源与权限,需先在Keycloak后台创建对应UMA资源,并确保Token拥有访问该资源的权限
- 请求参数缺失或格式错误,比如未传入
resource_id等UMA流必填参数
内容的提问来源于stack exchange,提问作者Oliver
相关产品推荐
相关产品推荐

