IdentityServer4声明不属于混合流令牌的一部分

时间:2019-02-27 16:59:10

标签: c# asp.net-identity identityserver4

我正在尝试构建混合流,并使用IdentityServer4对返回的访问令牌进行声明。我正在使用QuickStart UI控件。

在用户成功通过身份验证后,在我的AccountController中,我有以下代码用于登录:

await HttpContext.SignInAsync("anon@nymous.com", "anon@nymous.com", null, new Claim("MyName", "Ophir"));

在导致此流程的MVC网站中,在我要“保护”的页面上,我具有以下代码:

[Authorize]
public IActionResult RestrictedMvcResource()
{
       var token = _httpContext.HttpContext.GetTokenAsync("access_token").Result;
       var identity = User.Identity;
       return View();
}

成功登录后,调试器会将此代码正常运行,并且我获得了访问令牌。

问题是,如果我解码访问令牌(我正在使用https://jwt.io/),则可以看到名称和主题,但看不到我已定义的MyName声明。

(我的系统中还有client_credentials的另一个流程,该流程确实返回了令牌的声明-但它使用了不同的代码流程)。

如何为混合流程返回代币上的声明?

编辑:

解决此问题是两件事的结合:

  1. 按照(选定)答案的建议实施IProfileService。这是我的实现方式:
public class ProfileService : IProfileService
{
    public Task GetProfileDataAsync(ProfileDataRequestContext context)
    {
        context.AddRequestedClaims(context.Subject.Claims);

        foreach (Claim claim in context.Subject.Claims)
        {
            if (context.IssuedClaims.Contains(claim))
                continue;

            context.IssuedClaims.Add(claim);
        }

        return Task.FromResult(0);
    }

    public Task IsActiveAsync(IsActiveContext context)
    {
        context.IsActive = true;
        return Task.FromResult(0);
    }
}

这将添加令牌上尚未存在的所有声明。

  1. 在致电HttpContext.SignInAsync时,您必须传递声明列表,否则context.Subject.Claims集合中将没有其他声明。

3 个答案:

答案 0 :(得分:3)

如果要向令牌添加自定义声明,则可以实现自定义IProfileService

您可以在Identity Server 4 docs中找到更多信息。

简单的自定义配置文件服务的示例为:

public class CustomProfileService : IProfileService
{
    public Task GetProfileDataAsync(ProfileDataRequestContext context)
    {
        context.AddRequestedClaims(context.Subject.Claims);
        context.IssuedClaims.Add(new Claim("MyName", "Ophir"));
        return Task.FromResult(0);
    }

    public Task IsActiveAsync(IsActiveContext context)
    {
        context.IsActive = true;    
        return Task.FromResult(0);
    }
}

有了这个,只需将其注册到DI:

services.AddTransient<IProfileService, CustomProfileService>();

每当请求access_tokenid_token时,它将被调用。如果您只想要某些类型的令牌中的额外声明,则需要按照Ruard的评论检查context.Caller

编辑: 另外,也可以按照Identity Server 4 quickstarts之一的示例,将声明直接添加到用户配置中:

            new TestUser
            {
                SubjectId = "1",
                Username = "alice",
                Password = "password",

                Claims = new []
                {
                    new Claim("MyName", "Ophir")
                }
            },

如果最终没有实现自定义IProfileService并继续使用DefaultProfileService,那么您还需要在配置中添加自定义IdentityResource

return new List<IdentityResource>
{
    //..Your other configured identity resources

    new IdentityResource(
    name: "custom.name",
    displayName: "Custom Name",
    claimTypes: new[] { "MyName" });
};

任何希望在令牌中添加此声明的客户都需要请求custom.name范围。

答案 1 :(得分:0)

AspNet.Security.OpenIdConnect.Server不会序列化未设置目标的声明。我在使用OpenIdDict时遇到了这个问题。

尝试一下:

var claim = new Claim("MyName", "Ophir");
claim.SetDestinations(OpenIdConnectConstants.Destinations.AccessToken);

await HttpContext.SignInAsync("anon@nymous.com", "anon@nymous.com", null, claim);

您可能需要添加以下名称空间:

using AspNet.Security.OpenIdConnect.Extensions;
using AspNet.Security.OpenIdConnect.Primitives;

答案 2 :(得分:0)

我通过两个步骤为身份服务器的令牌添加声明。

通过像其他答案之一所示的自定义配置文件服务,您可以为用户定义自己的声明。

然后可以通过userinfo端点请求这些声明。

或者您创建一个名为IncludeNameInAccessToken的Api(资源),如果您请求将Api作为范围,则默认将名称声明添加到访问令牌中。