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

rlc*_*ews 5 c# claims-based-identity identityserver3

我正在尝试创建一个自定义用户,对其进行身份验证,然后使用身份服务器3将用户声明返回到有角度的应用程序中。我查看了示例,尤其是CustomUserService项目。

编辑我已根据进度将问题更新为:

在starup.cs文件中,我具有作用域,并且正在加载客户端

var idServerServiceFactory = new IdentityServerServiceFactory()
                    .UseInMemoryClients(Clients.Get())
                    .UseInMemoryScopes(Scopes.Get());
                   //.UseInMemoryUsers(Users.Get());
Run Code Online (Sandbox Code Playgroud)

然后在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);
    }
Run Code Online (Sandbox Code Playgroud)

我遇到麻烦的地方是GetProfileDataAsync方法。我无法弄清楚如何向客户端添加声明,或者是否需要调用第二个客户端。

该应用程序是一个向Idsrv进行身份验证的有角度的客户端。我创建了一个客户

//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 
                }
                }
Run Code Online (Sandbox Code Playgroud)

这里发布SO之后,我试图将声明添加到返回给客户端的访问令牌中。

我定义的范围仅用于资源。我认为我需要创建第二个类似于注释部分的身份?

            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)
                //        }
                //    }
                };
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

上面指出了我要作为声明返回的信息(姓名,给定名称,姓氏电子邮件)。

试图遵循前面提到的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);
            }
        }
Run Code Online (Sandbox Code Playgroud)

这就是我被困的地方,当我看到在中定义的声明时,context.RequestedClaimTypes永远不会匹配,claim.Type因为它始终包含idsrv声明的基本属性。

由于这是一个自定义数据库,所有与索赔有关的信息都存储在数据库的“用户”表中,而不是在“索赔”表中。我试图将用户表中每个字段的值映射到Claim值中。

更新 我已经返回并取消了对范围的注释,并将以下声明添加到标识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),
                        }
                    }
Run Code Online (Sandbox Code Playgroud)

初始身份验证有效,并且在回调上,我可以看到评估的范围。但是,我仍然对如何从数据库中获取应用于这些作用域的值仍然停留。这部分代码正在(按预期方式)查找System.Security.Claims.Claim类型。现在,这是确定如何获取索赔值的停止点,索赔值是我的用户应用并作为索赔返回的属性。

在这一点上,我被重定向回有角度的应用程序,但是访问令牌对象对于用户信息是空白的。

我现在该如何将自己的值插入context.IssuedClaimsas声明中?

提前致谢

Joh*_*nes 2

关于您似乎正在尝试做的事情的一些评论。

1 您正在复制现有范围,即profile范围。

iuser如果您想要的所有声明都在 OIDC 标准范围内(此处:profile/ ),则无需定义自己的范围email。您声明的身份范围中存在的声明iuser已包含在profile标准的范围内。因此,只需询问范围profile,并允许您的客户请求该范围。

标准范围及其声明内容: http://openid.net/specs/openid-connect-core-1_0.html#StandardClaims

2AllowedScopes你的客户端配置有点错误。

请改用以下常量。

AllowedScopes = new List<string>
{
    Constants.StandardScopes.OpenId,
    Constants.StandardScopes.Profile,
    Constants.StandardScopes.Email,
}
Run Code Online (Sandbox Code Playgroud)

另请参阅此示例配置。 https://github.com/IdentityServer/IdentityServer3.Samples/blob/master/source/_sharedConfiguration/Clients.cs

3 你还没有真正展示如何获取 id_tokens..

..但请记住向 idsrv 提供您想要返回 id_token 的请求,包括来自范围的声明profile。换句话说/authorize,向/userinfo或端点的请求必须包含一个值至少为 的参数。/tokenscopeopenid profile

例如,就像 Brock A. 在他的 oidc js 客户端库中所做的那样: https://github.com/IdentityServer/IdentityServer3.Samples/blob/master/source/Clients/JavaScriptImplicitClient/app.js#L18