IdentityServer4 正在返回一个有效令牌,无论用户 ID 和密码组合是否有效

IdentityServer4 is returning a valid token whether the userid and password combinations are valid or not

我正在编写一个 Angular4 应用程序,我想将 RestFul API 与 IdentityServer4 一起用于 authentication/authorization。为了开始这个过程,我下载了 GitHub IdentityServer4Demo 项目。我完成了演示并决定添加 ResourceOwnerPasswordValidatorProfileService 服务来验证应该有权访问该应用程序的用户。我的问题是,现在所有 userid/password 组合都会触发来自 IdentityServer 的有效令牌 无论用户是否有效 。我在这里错过了什么? the userid and password should be alice to get an access token Startup.cs

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc();

        var builder = services.AddIdentityServer()
            .AddInMemoryApiResources(Config.GetApis())
            .AddInMemoryIdentityResources(Config.GetIdentityResources())
            .AddInMemoryClients(Config.GetClients());
        //                .AddTestUsers(TestUsers.Users);
        services.AddTransient<IProfileService, Configuration.ProfileService>();
        services.AddTransient<IResourceOwnerPasswordValidator, Configuration.ResourceOwnerPasswordValidator>();


        // demo versions
        services.AddTransient<IRedirectUriValidator, DemoRedirectValidator>();
        services.AddTransient<ICorsPolicyService, DemoCorsPolicy>();

        if (_env.IsDevelopment())
        {
            builder.AddTemporarySigningCredential();
        }
        else
        {
            builder.AddTemporarySigningCredential();
            //builder.AddSigningCredential("6B7ACC520305BFDB4F7252DAEB2177CC091FAAE1", StoreLocation.CurrentUser, nameType: NameType.Thumbprint);
        }
    }

ResourceOwnerPasswordValidator.cs

      public Task ValidateAsync(ResourceOwnerPasswordValidationContext context)
    {
        using (IDbConnection db = new SqlConnection("Data Source=server1;Initial Catalog=myDB;Integrated Security=SSPI;"))
        {
            var user = db.Query<User>("select * from Users where UserName=@UserName and Password=@Password",
                new { UserName = context.UserName, Password = context.Password }).SingleOrDefault<User>();
            if (user == null)
            {
                context.Result = new GrantValidationResult(IdentityModel.OidcConstants.TokenErrors.UnauthorizedClient, "Invalid User of Password.");
                return Task.FromResult<ResourceOwnerPasswordValidationContext>(context);
            }
            else
            {
                context.Result = new GrantValidationResult(user.Id.ToString(), "password");
                return Task.FromResult<ResourceOwnerPasswordValidationContext>(context);
            }

        }
    }

ProfileService.cs

     public class ProfileService : IProfileService
{

    public Task GetProfileDataAsync(ProfileDataRequestContext context)
    {
        context.IssuedClaims = context.Subject.Claims.ToList();
        //context.IssuedClaims.Add(new Claim("test-claim", "test-value"));
        return Task.FromResult(0);
    }

    public Task IsActiveAsync(IsActiveContext context)
    {
        return Task.FromResult(0);
    }
}

Config.cs

     public static IEnumerable<Client> GetClients()
    {
        return new List<Client>
        {
            new Client
            {
                ClientId = "client1",
                RequireClientSecret = false,
                //ClientSecrets = { new Secret("secret".Sha256()) },
                //AccessTokenLifetime = 3600,
                //AlwaysSendClientClaims=false,
                AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
                AllowedScopes = { "openid", "profile", "email", "api","api1"  },
                                    AllowOfflineAccess = true
            },

不确定为什么要 angular 应用的 ResourceOwnerPassword 流。您应该使用隐式或混合解决方案。

The spec recommends using the resource owner password grant only for “trusted” (or legacy) applications. Generally speaking you are typically far better off using one of the interactive OpenID Connect flows when you want to authenticate a user and request access tokens.

无论如何,here 是一个很好的示例应用程序,您可以查看一下,可能有助于解决您的问题。

当您在行 var user = db.Query<User>("select * from Users where UserName=@UserName and Password=@Password" 上放置一个断点时,当您使用无效的 username/password 调用它时,User 有什么值?它是空的吗?如果不是,那么问题出在您的 select/tables 上,因为它 寻找用户。

如果它不为空,则问题在于您如何返回 Task.

在我们的实现中,我们正在为失败做如下事情:

context.Result = new GrantValidationResult(TokenRequestErrors.UnauthorizedClient);
return Task.FromResult(false);

以及类似这样的成功...

context.Result = new GrantValidationResult(user.UserName, "password", claims);
return Task.FromResult(0);