[英]How to add claims to access token for identityserver3 using custom user service
I am trying to create a custom user, authenticate them and then return the user claims into an angular application using identity server 3. I've looked over the samples, specifically the CustomUserService project. 我正在尝试创建自定义用户,对其进行身份验证,然后使用身份服务器3将用户声明返回到角度应用程序中。我查看了示例,特别是CustomUserService项目。
Edit I've updated question based on progress to: 编辑我根据进度更新了问题:
Within the starup.cs file I have the scopes, and clients loading 在starup.cs文件中,我有范围,客户端加载
var idServerServiceFactory = new IdentityServerServiceFactory()
.UseInMemoryClients(Clients.Get())
.UseInMemoryScopes(Scopes.Get());
//.UseInMemoryUsers(Users.Get());
Then the override in UserServices.cs 然后是UserServices.cs中的覆盖
public override Task AuthenticateLocalAsync(LocalAuthenticationContext context)
{
string hashedPassword = string.Empty;
string salt = string.Empty;
//pull users
using (IdentityModelContext ctx = new IdentityModelContext())
{
IIdSrvUserRepository ur = new IdSrvUserRepository(ctx);
Users = ur.GetAll().ToList();
}
//salt curent password
var user = Users.SingleOrDefault(x => x.UserName == context.UserName);
if (user != null)
{
salt = user.Salt;
hashedPassword = IqUtils.GenerateSaltedPassword(context.Password, salt);
if (user.UserName == context.UserName && hashedPassword == user.Password)
{
try
{
context.AuthenticateResult = new AuthenticateResult(user.Subject, user.UserName);
}
catch (Exception ex)
{
string msg = $"<-- Login Error: Message: {ex.Message}, Inner Exception:{ex.InnerException} --->";
myLogger log = new myLogger("IdentityServer");
}
}
}
return Task.FromResult(0);
}
Where I am having trouble is on the GetProfileDataAsync method. 我遇到问题的地方是GetProfileDataAsync方法。 I cannot figure out how to add claims to the client or if I need to invoke a second client. 我无法弄清楚如何向客户端添加声明或者我是否需要调用第二个客户端。
The application is an angular client authenticating to Idsrv. 该应用程序是一个对Idsrv进行身份验证的角度客户端。 I have one client created 我创建了一个客户端
//Angular client authentication
new Client
{
ClientId = "iqimplicit",
ClientName = "IQ Application (Implicit)",
Flow = Flows.Implicit,
AllowAccessToAllScopes = true,
IdentityTokenLifetime = 300,//default value is 5 minutes this is the token that allows a user to login should only be used once
AccessTokenLifetime = 3600, // default is one hour access token is used for securing routes and access to api in IQ
RequireConsent = false,
RequireSignOutPrompt = true,
AllowedScopes = new List<string>
{
//no clue if this is correct
StandardScopes.Profile.Name
},
RedirectUris = new List<string>
{
angularClient + "callback.html"
},
PostLogoutRedirectUris = new List<string>()
{
//redirect to login screen
angularClient
}
}
Following the SO posting here I was trying to add the claims to the access token returned to the client. 在这里发布SO后,我试图将声明添加到返回给客户端的访问令牌。
The scope I have defined is just for the Resource. 我定义的范围仅适用于资源。 I assume I need to create a second for identity similar to the commented out section? 我假设我需要创建一个类似于注释部分的身份的第二个?
return new List<Scope>
{
StandardScopes.OpenId,
StandardScopes.ProfileAlwaysInclude,
StandardScopes.Address,
new Scope
{
Name = "appmanagement",
DisplayName = "App Management",
Description = "Allow application to management.",
Type = ScopeType.Resource,
Claims = new List<ScopeClaim>()
{
new ScopeClaim("role", false),
new ScopeClaim("Name", true),
new ScopeClaim("GivenName", true),
new ScopeClaim("FamilyName", true),
new ScopeClaim("Email", true),
},
},
// new Scope
// {
// Name = "iquser",
// DisplayName = "User",
// Description = "Identifies the user",
// Type = ScopeType.Identity,
// Claims = new List<ScopeClaim>()
// {
// new ScopeClaim("Name", true)
// }
// }
};
}
}
}
The information I want to pass back as claims are indicated above, (name, givenname, familyname email). 我想要作为声明传回的信息如上所示(姓名,名字,姓氏电子邮件)。
Attempting to follow the comments from the previously mentioned SO postI overrode the GetProfileDataAsync
which should be getting the information for the user. 试图遵循前面提到的SO postI的注释,覆盖了应该为用户获取信息的GetProfileDataAsync
。
public override Task GetProfileDataAsync(ProfileDataRequestContext context)
{
// issue the claims for the user
var user = Users.SingleOrDefault(x => x.Subject == context.Subject.GetSubjectId());
if (user != null)
{
if(context.RequestedClaimTypes != null)
try
{
if (context.RequestedClaimTypes != null)
{
List<Claim> newclaims = new List<Claim>();
foreach (Claim claim in context.Subject.Claims)
{
if (context.RequestedClaimTypes.Contains(claim.Type))
{
newclaims.Add(claim);
}
}
context.IssuedClaims = newclaims;
}
}
catch (Exception ex)
{
string msg = $"<-- Error Getting Profile: Message: {ex.Message}, Inner Exception:{ex.InnerException} --->";
myLogger log = new myLogger("IdentityServer");
}
}
//return Task.FromResult(context.IssuedClaims);
return Task.FromResult(0);
}
}
And this is where I am stuck While I see the claims I defined in the context.RequestedClaimTypes
There is never a match in claim.Type
as it always contains the base properties of the idsrv claim. 而这正是我坚持当我看到我在规定的索赔context.RequestedClaimTypes
从来就没有在比赛中claim.Type
因为它总是包含idsrv要求的基本属性。
Since this is a custom database all information that is going on the claims is stored in the Users table of the database and not within a Claims table. 由于这是一个自定义数据库,因此声明中的所有信息都存储在数据库的Users表中,而不是存储在Claims表中。 I was trying to map from the user table the values of each field into the Claim value. 我试图从用户表中将每个字段的值映射到Claim值。
Update I've gone back and uncomment from the scope and added the following claims to an identity scopeType 更新我已退回并取消注释范围,并将以下声明添加到标识scopeType
new Scope
{
Name = "iuser",
DisplayName = "User",
Description = "Identifies the user",
Type = ScopeType.Identity,
Claims = new List<ScopeClaim>()
{
new ScopeClaim(Constants.ClaimTypes.Name, alwaysInclude: true),
new ScopeClaim(Constants.ClaimTypes.GivenName, alwaysInclude: true),
new ScopeClaim(Constants.ClaimTypes.FamilyName, alwaysInclude: true),
new ScopeClaim(Constants.ClaimTypes.Email, alwaysInclude: true),
}
}
Initial Authentication works and on the callback I can see the scopes evaluated. 初始身份验证工作,并在回调我可以看到评估的范围。 However I'm still stuck on how I would now get the values from the DB applied to these scopes. 但是我仍然坚持我现在如何从应用于这些范围的数据库中获取值。 This section of code is (as expected) looking for the System.Security.Claims.Claim
type. 这段代码(正如预期的那样)寻找System.Security.Claims.Claim
类型。 This is now the stopping point in determining how to get the claim values which are properties on my user applied and returned as claims. 现在,这是确定如何获取我的用户属性的声明值并作为声明返回的停止点。
At this point I am redirected back to the angular app but the accesstoken object is blank for user information. 此时,我被重定向回角度应用程序,但是对于用户信息,accesstoken对象是空白的。
How do I go about inserting my own values into the context.IssuedClaims
as claims at this point? 我如何将自己的值插入context.IssuedClaims
作为此时的声明?
Thanks in advance 提前致谢
A couple of comments on what you seem to be trying to do. 关于你似乎想做什么的一些评论。
profile
scope. 1您正在复制现有范围,即profile
范围。 You don't need to define your own scope iuser
, if all the claims you want are in a OIDC standard scopes (here: profile
/ email
). 如果您想要的所有声明都在OIDC标准范围内(此处: profile
/ email
),则无需定义自己的范围iuser
。 The claims you're declaring present in your iuser
identity scope is already covered by the profile
scope in the standard. 您在iuser
身份范围中声明的声明已包含在标准中的profile
范围内。 So just ask for the profile
scope instead, and make your client be allowed to request that scope. 因此,只需要询问profile
范围,并允许您的客户端请求该范围。
Standard scopes, and what their claim conents are: http://openid.net/specs/openid-connect-core-1_0.html#StandardClaims 标准范围,以及它们的主张是什么: http ://openid.net/specs/openid-connect-core-1_0.html#StandardClaims
AllowedScopes
configuration on your client configuration is a bit wrong. 2客户端配置上的AllowedScopes
配置有点不对。 Use the following constants instead. 请改用以下常量。
AllowedScopes = new List<string>
{
Constants.StandardScopes.OpenId,
Constants.StandardScopes.Profile,
Constants.StandardScopes.Email,
}
See also this sample config. 另请参见此示例配置。 https://github.com/IdentityServer/IdentityServer3.Samples/blob/master/source/_sharedConfiguration/Clients.cs https://github.com/IdentityServer/IdentityServer3.Samples/blob/master/source/_sharedConfiguration/Clients.cs
.. but remember to supply your request towards idsrv that you want a id_token back including claims from the profile
scope. ..但请记住向idsrv提供您想要id_token的请求,包括来自profile
范围的声明。 The request towards /authorize
, /userinfo
or /token
endpoint must in other words inlcude a scope
param with a value of at least openid profile
. 换句话说,对/authorize
, /userinfo
或/token
端点的请求必须包含至少具有openid profile
值的scope
参数。
For example, like Brock A. does in his oidc js client library: https://github.com/IdentityServer/IdentityServer3.Samples/blob/master/source/Clients/JavaScriptImplicitClient/app.js#L18 例如,像Brock A.在他的oidc js客户端库中做的那样: https : //github.com/IdentityServer/IdentityServer3.Samples/blob/master/source/Clients/JavaScriptImplicitClient/app.js#L18
At a first glance it looks like your angular client is not configured for requesting the resource scope iuser
you are only configuring the requesting of the name
identity scope. 乍一看,您的角度客户端看起来没有配置为请求资源范围iuser
您只配置了name
标识范围的请求。 Add that scope to the list of allowed scopes of client iqimplicit
and make sure to add that scope to the client configuration on your angular application. 将该范围添加到客户端iqimplicit
的允许范围列表中,并确保将该范围添加到角度应用程序的客户端配置中。
Working through this some more I was finally able to figure out one approach for adding a claim. 通过这个工作,我终于能够找到一种方法来添加索赔。
Within the UserService
class on the GetProfileDataAsync()
I ended up being able to create and add a claim using the following. 在GetProfileDataAsync()
的UserService
类中,我最终能够使用以下内容创建和添加声明。
var user = Users.SingleOrDefault(x => x.Subject == context.Subject.GetSubjectId());
if (user != null)
{
try
{
//add subject as claim
var claims = new List<Claim>
{
new Claim(Constants.ClaimTypes.Subject, user.Subject),
};
//get username
UserClaim un = new UserClaim
{
Subject = Guid.NewGuid().ToString(),
ClaimType = "username",
ClaimValue = string.IsNullOrWhiteSpace(user.UserName) ? "unauth" : user.UserName
};
claims.Add(new Claim(un.ClaimType, un.ClaimValue));
//get email
UserClaim uem = new UserClaim
{
Subject = Guid.NewGuid().ToString(),
ClaimType = "email",
ClaimValue = string.IsNullOrWhiteSpace(user.EmailAddress) ? string.Empty : user.EmailAddress
};
context.IssuedClaims = claims;
}
catch (Exception ex)
{
string msg = $"<-- Error Getting Profile: Message: {ex.Message}, Inner Exception:{ex.InnerException} --->";
myLogger log = new myLogger("IdentityServer");
}
}
//return Task.FromResult(context.IssuedClaims);
return Task.FromResult(0);
The issue I had was more in the model. 我遇到的问题更多的是在模型中。 All the information which would normally be saved in a Claims store was stored on the user itself so when the profile is created it was at this point I had to pull the values from the user and create/add them as claims. 通常保存在Claims存储中的所有信息都存储在用户自身上,因此在创建配置文件时,我必须从用户提取值并创建/添加它们作为声明。
The OIDC manager in the angular client then grants me access to the claims on tehe access token and their values using the following: 角客户端中的OIDC管理器然后授予我访问权限访问令牌及其值的权限,使用以下内容:
$scope.mgr.oidcClient.loadUserProfile($scope.mgr.access_token)
.then(function (userInfoValues) {
$scope.profile = userInfoValues;
$scope.fullName = $scope.profile.given_name + ' ' + $scope.profile.family_name;
checkAdmin($scope.profile.sys_admin);
$scope.$apply();
});
Note that $apply()
is being used here only because this code exists inside of directive used across the application. 请注意,此处仅使用$apply()
因为此代码存在于整个应用程序中使用的指令内。
I'm not sure if this is the best implementation for adding claims but this approach does work. 我不确定这是否是添加声明的最佳实现,但这种方法确实有效。 For the time being I'll hold off on marking this as the answer to see if there are better approaches. 暂时我会推迟将此标记为答案,看看是否有更好的方法。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.