IdentityServer4 声明不是混合流上令牌的一部分
IdentityServer4 claims are not part of the token on hybrid flow
我正在尝试构建一个混合流程,并使用 IdentityServer4 声明 returned 访问令牌。我正在使用 QuickStart UI 控制器。
在我的 AccountController
中,用户成功通过身份验证后,我有以下代码让他登录:
await HttpContext.SignInAsync("anon@nymous.com", "anon@nymous.com", null, new Claim("MyName", "Ophir"));
在导致此流程的 MVC 网站中,在我想要 "protect" 的页面上,我有以下代码:
[Authorize]
public IActionResult RestrictedMvcResource()
{
var token = _httpContext.HttpContext.GetTokenAsync("access_token").Result;
var identity = User.Identity;
return View();
}
成功登录后,调试器成功命中此代码,我正在获取访问令牌。
问题是,如果我解码我的访问令牌(我正在使用 https://jwt.io/),我会看到名称和主题,但看不到我定义的 MyName
声明。
(我的系统中有另一个 client_credentials
流程,它 return 对令牌的声明 - 但它使用不同的代码流程)。
如何return 混合流程的令牌声明?
编辑:
解决这个问题需要结合两件事:
- 按照(选定的)答案中的建议实施
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
集合中不会有其他声明。
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;
如果您想向令牌添加自定义声明,您可以实施自定义 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
范围。
我通过两个步骤向身份服务器的令牌添加声明。
通过像其他答案之一所示的自定义配置文件服务,您可以为用户定义自己的声明。
然后可以通过 userinfo 端点请求这些声明。
或者您创建一个名为 IncludeNameInAccessToken 的 Api(资源),如果您请求 Api 作为范围,它会默认将名称声明添加到访问令牌。
我正在尝试构建一个混合流程,并使用 IdentityServer4 声明 returned 访问令牌。我正在使用 QuickStart UI 控制器。
在我的 AccountController
中,用户成功通过身份验证后,我有以下代码让他登录:
await HttpContext.SignInAsync("anon@nymous.com", "anon@nymous.com", null, new Claim("MyName", "Ophir"));
在导致此流程的 MVC 网站中,在我想要 "protect" 的页面上,我有以下代码:
[Authorize]
public IActionResult RestrictedMvcResource()
{
var token = _httpContext.HttpContext.GetTokenAsync("access_token").Result;
var identity = User.Identity;
return View();
}
成功登录后,调试器成功命中此代码,我正在获取访问令牌。
问题是,如果我解码我的访问令牌(我正在使用 https://jwt.io/),我会看到名称和主题,但看不到我定义的 MyName
声明。
(我的系统中有另一个 client_credentials
流程,它 return 对令牌的声明 - 但它使用不同的代码流程)。
如何return 混合流程的令牌声明?
编辑:
解决这个问题需要结合两件事:
- 按照(选定的)答案中的建议实施
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
集合中不会有其他声明。
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;
如果您想向令牌添加自定义声明,您可以实施自定义 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
范围。
我通过两个步骤向身份服务器的令牌添加声明。
通过像其他答案之一所示的自定义配置文件服务,您可以为用户定义自己的声明。
然后可以通过 userinfo 端点请求这些声明。
或者您创建一个名为 IncludeNameInAccessToken 的 Api(资源),如果您请求 Api 作为范围,它会默认将名称声明添加到访问令牌。