You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 23:46:26