尝试将 IdentityServer4 添加为具有自定义用户存储的 Javascript 应用程序的身份授权机构

Trying to add IdentityServer4 as identity authority for a Javascript application with custom user store

我已经按照 IdentityServer4 文档使用默认测试配置(使用内存中的客户端和用户)创建了一个基本的本地实现。此时IS4初始化如下:

services.AddIdentityServer()
    .AddDeveloperSigningCredential()
    .AddInMemoryIdentityResources(Config.GetIdentityResources())
    .AddInMemoryApiResources(Config.GetApiResources())
    .AddInMemoryClients(Config.GetClients())
    .AddTestUsers(Config.GetUsers());

然后我 followed the instructions for creating a simple Javascript application using the oidc-connect 图书馆。这工作正常,所以现在我有一个 Javascript 应用程序,它允许用户通过我的 IS4 实例登录。此 JS 应用程序在 IS4 中表示为根据说明使用隐式流的客户端。

我现在想将我的 IS4 实例与我的真实用户存储集成。阅读了几篇文章后,我似乎需要根据 和其他各种文章为 IResourceOwnerPasswordValidatorIProfileService 提供实现。

我已经这样做了,现在只使用一个虚拟用户存储库,它与一组虚拟内存用户一起工作,而不是一个真正的外部存储。我的 IS4 初始化现在看起来像这样:

services.AddIdentityServer()
    .AddDeveloperSigningCredential()
    .AddInMemoryIdentityResources(Config.GetIdentityResources())
    .AddInMemoryApiResources(Config.GetApiResources())
    .AddInMemoryClients(Config.GetClients());

builder.Services.AddTransient<IProfileService, CustomProfileService>();
builder.Services.AddTransient<IResourceOwnerPasswordValidator, CustomResourceOwnerPasswordValidator>();

现在,当我返回并测试我的 Javascript 应用程序时,这就是它变得奇怪的地方。尝试登录时,我被带到了我的 IS4 登录屏幕。我输入了我希望使用的凭据,但是 无效的用户名或密码 被 return 编辑了。调试我可以看到代码从未进入 CustomResourceOwnerPasswordValidator 中的 ValidateAsync 方法。 但是,我偶然发现如果我输入 bobalice 作为用户名和密码(因此,例如,输入 bob 作为用户名 密码)然后我通过身份验证并且可以 return 返回我的应用程序。这没有任何意义。我确信这些是 IdentityServer4 示例内存用户使用的两个默认用户的用户名并非巧合,但您可以在上面看到我不再使用内存用户。事实上,即使我从 Config.cs.

中注释掉 GetUsers 方法,这仍然是正确的

我无法解释这一点,但无论如何我想要的是能够使用我的用户存储。所以我进一步阅读并看到建议我只能将 IResourceOwnerPasswordValidator 用于具有 GrantTypes.ResourceOwnerPassword 的客户。但是,当我在 IS4 中更改 Javascript 应用程序的客户端以使用此授权类型时,当我尝试登录时,我看到 unauthorized_client 错误。

所以我卡住了。我曾认为,由于我的设置与内存中的用户一起使用,因此使用真实的用户存储不会有太多工作,但显然我缺少一些东西。非常感谢您的指导。

您不需要将 ProfileService 作为服务的依赖项注入。

你需要这样的东西:

services.AddIdentityServer()
            .AddProfileService<CustomProfileService>()
            //..... more code

此外 - 您的 CustomProfileService 不需要实现 IProfileService(该方法接受泛型)。

真正的"magic"发生在AccountController中:

/// <summary>
    /// Handle postback from username/password login
    /// </summary>
    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> Login(LoginInputModel model)
    {
        // logic
    }

在那里,在 LoginInputModel 中你有用户名、密码等。你需要验证,所以最后没有真正需要 IProfileService 如果你正在编写你的自定义用户管理。