我正在尝试构建混合流,并使用IdentityServer4声明返回的访问令牌。我正在使用QuickStart UI控制器。
在用户成功通过身份验证后的AccountController
中,我有以下代码签署他:
await HttpContext.SignInAsync("[email protected]", "[email protected]", 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
,它确实返回了令牌上的声明 - 但它使用了不同的代码流)。
如何返回混合流程令牌的声明?
编辑:
解决这个问题是两件事的组合:
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);
}
}
这将添加令牌上尚未存在的任何声明。
HttpContext.SignInAsync
时,您必须通过索赔清单,否则context.Subject.Claims
系列中不会有其他索赔。如果要向令牌添加自定义声明,可以实现自定义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_token
或id_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
范围。
AspNet.Security.OpenIdConnect.Server不会序列化没有设置目标的声明。我在使用OpenIdDict时遇到了这个问题。
试试这个:
var claim = new Claim("MyName", "Ophir");
claim.SetDestinations(OpenIdConnectConstants.Destinations.AccessToken);
await HttpContext.SignInAsync("[email protected]", "[email protected]", null, claim);
您可能需要添加这些名称空间:
using AspNet.Security.OpenIdConnect.Extensions;
using AspNet.Security.OpenIdConnect.Primitives;
我有两个步骤向我们的身份服务器添加对令牌的声明。
通过自定义配置文件服务(如其他答案之一),您可以为用户定义自己的声明。
然后可以通过userinfo端点请求这些声明。
或者,您创建一个名为IncludeNameInAccessToken的Api(资源),如果您请求将Api作为范围,则默认情况下会将名称声明添加到访问令牌。