理想功能:用户登录并通过网站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信息-仅包含普通受众,范围,有效期等。
答案 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);