IdentityServer-如何向客户端证书令牌添加其他声明?

时间:2019-09-09 14:41:36

标签: c# identityserver4 identityserver3

理想功能:用户登录并通过网站A身份验证。他们单击一个按钮,后端从数据库中查找网站B中帐户的ID,然后将此信息发送到IdentityServer以创建一个JWT,其中包含“ user_id”字段。然后,它用于在网站B上调用REST端点并进行身份验证,然后使用“ user_id”创建登录cookie,该cookie将发送回网站A。然后,用户将被重定向。

我们正在运行IdentityServer 4,但是使用IdentityServer3作为主要代码库与它进行通信是在.NET Framework上。我尝试在extras参数中包含“ user_id”字段,但这似乎无济于事。

var client = new TokenClient(requestPath, CLIENT_ID, CLIENT_SECRET, 
  AuthenticationStyle.PostValues);


var test = new Dictionary<string, string>
{
  { "user_id", "123123" }
};

// request token
var tokenResponse = await client
  .RequestClientCredentialsAsync(apiScope, test)
  .ConfigureAwait(false);

我也尝试过使用client.RequestCustomAsync和client.RequestAsync,但是没有运气。

我收到的令牌没有问题,但是它不包含user_id信息-仅包含普通受众,范围,有效期等。

3 个答案:

答案 0 :(得分:1)

对于自定义问题字段,您需要创建一个IProfileService的继承类,然后实现方法GetProfileDataAsync。看起来像:

public class IdentityProfileService : IProfileService
{
    public async Task GetProfileDataAsync(ProfileDataRequestContext context)
    {
        var sub = context.Subject.GetSubjectId();
        // Call UserManager or any database to get more fields
        var user = await _userManager.FindByIdAsync(sub);

        // Set returned claims (System.Security.Claims.Claim) by setting context.IssuedClaims
        context.IssuedClaims = claims;
    }

    public async Task IsActiveAsync(IsActiveContext context)
    {
        context.IsActive = true;
    }
}

在注册Identity Service 4时,您需要对其进行声明(一个示例在.NET Core中,与.NET Framework相同)

var identityBuilder = services.AddIdentityServer().AddProfileService<IdentityProfileService>();

答案 1 :(得分:0)

我认为您应该检查ApiResource配置。无论您在ApiResource配置的UserClaims属性中添加什么声明,这些声明都会出现在访问令牌中。例如

 public IEnumerable<ApiResource> GetApiResources()
 {
      return new List<ApiResource>
      {
            new ApiResource("api1")
            {
                UserClaims = new[] { "CustomClaim1", "CustomClaim2"},
            }, 
       }
 }

在上面的代码中,访问代码将包含CustomClaim1和CustomClaim2。因此,如果您不提及它们,它们将不会出现在访问令牌中。

答案 2 :(得分:0)

这是对我有用的东西,主要是following this example

在IdentityServer中,创建一个新类UserInfoGrant,该类实现IExtensionGrantValidator,从请求中提取自定义声明,将其添加到声明中,然后继续

public async Task ValidateAsync(ExtensionGrantValidationContext context)
{
  var userId = context.Request.Raw[UserIdKey];
  ...
  var claims = new List<Claim>
  {
    new Claim(UserIdKey, userId)
  }
  context.Result = new GrantValidationResult(sub, GrantType, claims);
}

然后我将该类添加到依赖注入

builder.AddExtensionGrantValidator<UserInfoGrant>();

我还有一个ProfileService类,该类实现了IProfileService,该声明将声明添加到返回的令牌中

public virtual async Task GetProfileDataAsync(ProfileDataRequestContext context)
{
  var authenticationType = context.Subject.Identities.First().AuthenticationType;
  var isCustomAuthenticationType = authenticationType.Equals(CustomGrantName,
    StringComparison.OrdinalIgnoreCase);
  if (isCustomAuthenticationType)
  {
    var claimsToAdd = context.Subject.Identities.First().Claims;
    context.IssuedClaims = claimsToAdd.ToList();
  }
  else { ... }

此ProfileService也已添加到DI

builder.AddProfileService<Helpers.ProfileService<TUser>>();

我还将自定义授予类型添加到了将要使用它的客户端。

最后,在网站A的调用代码中,我要求使用以下代码:

var tokenResponse = await client.RequestTokenAsync(new TokenRequest {
                    Address = disco.TokenEndpoint,
                    ClientId = CLIENTID,
                    ClientSecret = CLIENTSECRET,
                    GrantType = "custom_grant_name",
                    Parameters = {
                        { "scope", PROTECTED_RESOURCE_NAME },
                        { "user_id", "26616" }
                      }
                    }).ConfigureAwait(false);