如何在ClientCredentials流的Access Token中添加sub声明及解决后续异常?
在ClientCredentials流中添加
sub声明后触发ProfileService的问题解决 问题背景
我在机器对机器通信的ClientCredentials流中,需要为Access Token添加sub声明——因为AWS/GCP默认会包含这个声明,缺少它的话,扮演AWS角色会失败,且sub的值必须和client_id一致。
我尝试了两种添加sub声明的方式:
- 继承实现
ICustomTokenRequestValidator:
if (context.Result.ValidatedRequest.GrantType == "client_credentials") { // 为客户端添加"sub"声明 var clientId = context.Result.ValidatedRequest.Client.ClientId; var subClaim = new Claim("sub", clientId); context.Result.ValidatedRequest.ClientClaims.Add(subClaim); }
- 重写
DefaultTokenService:
var result = await base.CreateAccessTokenAsync(request); if (result != null && result.SubjectId == null) { result.Claims.Add(new Claim(JwtClaimTypes.Subject, request.ValidatedRequest?.ClientId)); }
但修改后出现了新问题:IdentityServer现在会触发原本只在用户交互流程中才会调用的ProfileService,而ASP.NET Identity的UserManager调用FindByIdAsync时,会因为sub声明是字符串(client_id),在尝试转换为int类型时失败。
解决方法
问题核心是添加sub声明后,IdentityServer误将其识别为用户主体ID,进而触发ProfileService加载用户信息,但ClientCredentials流本质是无用户的,无需该流程。以下是两种可行的解决思路:
1. 在ProfileService中区分请求类型,跳过ClientCredentials流的用户逻辑
在自定义ProfileService的两个核心方法里,先判断当前授权类型,若是client_credentials则直接返回,不执行用户查找:
public async Task GetProfileDataAsync(ProfileDataRequestContext context) { if (context.Request.Client?.AllowedGrantTypes.Contains(GrantType.ClientCredentials) == true) { return; } // 原有用户信息加载逻辑 var user = await _userManager.GetUserAsync(context.Subject); var claims = await _userManager.GetClaimsAsync(user); context.IssuedClaims = claims; } public async Task IsActiveAsync(IsActiveContext context) { if (context.Request.Client?.AllowedGrantTypes.Contains(GrantType.ClientCredentials) == true) { context.IsActive = true; return; } // 原有用户活跃状态判断逻辑 var user = await _userManager.GetUserAsync(context.Subject); context.IsActive = user != null && user.EmailConfirmed; }
2. 调整sub声明添加方式,避免触发用户主体关联
在添加sub声明时,确保仅将其作为客户端声明加入,同时保持SubjectId为null,让IdentityServer知道这是无用户的ClientCredentials流:
public async Task ValidateAsync(CustomTokenRequestValidationContext context) { if (context.Result.ValidatedRequest.GrantType == GrantType.ClientCredentials) { var clientId = context.Result.ValidatedRequest.Client.ClientId; context.Result.ValidatedRequest.ClientClaims.Add(new Claim(JwtClaimTypes.Subject, clientId)); // 明确设置Subject为null,防止触发ProfileService context.Result.ValidatedRequest.Subject = null; } }
内容的提问来源于stack exchange,提问作者unsafePtr
相关产品推荐
相关产品推荐

