没有自定义声明的access_token,声明仍然从userinfo返回

时间:2019-08-14 13:00:33

标签: c# asp.net-core identityserver4

Tl; dr::如何防止自定义声明出现在访问令牌中,并且仅在调用userinfo端点时返回?

我有一个IdentityServer4(使用authorization_code授予类型),在其中,在成功进行第三方身份验证时,我获得了有关用户的一些其他信息。我将这些声明存储为声明,将用户登录到IS4服务中并将这些声明添加到用户。声明已连接到名为“ custom”的身份资源范围

var myUser = some3rdParty.GetUserData();

Claim[] claims = {
    new Claim("custom.value1", myUser.Custom1),
    new Claim("custom.value2", myUser.Custom2)
};

await _events.RaiseAsync(new UserLoginSuccessEvent("myOidcProviderIS4", myUser.Id, myUser.Id, myUser.Fullname));
await _httpContextAccessor.HttpContext.SignInAsync(
    myUser.Id,
    myUser.Fullname,
    new AuthenticationProperties(),
    claims);

现在,当我检索访问令牌时,它不包含这些自定义声明,但是包含“自定义”范围。下一步将是调用userinfo端点以获取这些自定义声明。但是,默认情况下,对userinfo端点的调用不包括这些自定义声明。

因此,我创建了一个IProfileService,将声明复制到IssuedClaims中:

public class ProfileService : IProfileService
{
    public Task GetProfileDataAsync(ProfileDataRequestContext context)
    {
         List<Claim> claims = new List<Claim>();

        foreach (IdentityResource identityResource in context.RequestedResources.IdentityResources)
        {
            foreach (string userClaim in identityResource.UserClaims)
            {
                var claim = context.Subject.Claims.FirstOrDefault(c => c.Type == userClaim);
                if (claim != null)
                {
                    claims.Add(claim);
                }
            }
        }

        if (claims.Any())
        {
            context.IssuedClaims.AddRange(claims);
        }

        return Task.CompletedTask;
    }
}

现在,当我调用userinfo端点时,可见自定义声明。

但是在检查了访问令牌之后,我可以看到我的自定义声明也在那里可见。我觉得这有点多余,因为您(在这种情况下)实际上不必致电userinfo

如果没有自定义IProfileService实现(类仍在那儿,但只使用一个空方法),我做了以下观察:

  • context.Subject.Claims在调用token端点时包含自定义声明,但从userinfo调用时不包含自定义声明。
  • 数据库中的PersistedGrants表具有一个Data列,该列还包含自定义标记。

所以问题仍然是:如何防止我的自定义声明出现在访问令牌中,而仅当您调用userinfo端点时才返回?

3 个答案:

答案 0 :(得分:0)

我认为这可能是配置问题。

必须使用2个允许的范围定义客户端:

  • IdentityServerConstants.StandardScopes.OpenId
  • “自定义”
new Client
{
    AllowedScopes = new List<string>
    {
        IdentityServerConstants.StandardScopes.OpenId,
        "custom"
    },
    // etc...

然后在客户端上配置ResponseType = OpenIdConnectResponseType.IdTokenToken;

.AddOpenIdConnect(o =>
{
    o.Scope.Clear();
    o.Scope.Add("openid");
    o.Scope.Add("custom");
    o.ResponseType = OpenIdConnectResponseType.IdTokenToken;
    // etc...

答案 1 :(得分:0)

您可以检查呼叫者是否是/userinfo的调用者(我想这就是包含该属性的最初意图):

 if (context.Caller == IdentityServerConstants.ProfileDataCallers.UserInfoEndpoint)
        { 
            //custom logic to add requested claims 
            context.AddRequestedClaims(claims);
        }

答案 2 :(得分:0)

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

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

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