简体   繁体   English

如何使用自定义用户服务为identityserver3添加访问令牌的声明

[英]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. 关于你似乎想做什么的一些评论。

1 You're duplicating an existing scope, namely the 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

2 The 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

3 You haven't really shown how you fetch id_tokens.. 3你还没有真正展示你如何获取id_tokens ..

.. 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.

相关问题 使用没有密码的 IdentityServer3 生成访问令牌 - Generate access token with IdentityServer3 without password 使用 Microsoft.AspNetCore.ApiAuthorization.IdentityServer 时,如何在 JWT 和用户主体中添加自定义用户声明 - How do I add custom user claims in JWT and user principal when using Microsoft.AspNetCore.ApiAuthorization.IdentityServer Asp.Net Identity 2 用户信息如何映射到 IdentityServer3 配置文件声明 - How does Asp.Net Identity 2 User Info get mapped to IdentityServer3 profile claims 使用IdentityServer的自定义值获取所有声明的令牌 - Get token with all claims using a custom value with IdentityServer 如何将自定义声明添加到 azure b2c 访问令牌? - How to add custom claims to azure b2c access token? 如何让 IdentityServer 将用户身份添加到访问令牌? - How to make IdentityServer to add user identity to the access token? 如何自定义identityserver的cookie生成器? (将声明添加到令牌中,但不添加到 cookie 中) - How to customize the cookie generator of identityserver? (add claims into token but not into cookies) 如何在IdentityServer3中使用Ajax注销用户 - How To logout user with ajax in identityserver3 如何使用RestSharp使用JWT访问令牌和用户声明 - How to consume JWT access token and user claims using RestSharp IdentityServer4将声明添加到/ connect / token - IdentityServer4 Add Claims to /connect/token
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM