Попытка добавить IdentityServer4 в качестве органа идентификации для приложения Javascript с пользовательским хранилищем пользователей

Я следовал документации IdentityServer4, чтобы создать базовую локальную реализацию, используя конфигурацию теста по умолчанию (с клиентами и пользователями в памяти). На этом этапе IS4 инициализируется следующим образом:

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

Затем я следовал инструкциям по созданию простого приложения Javascript с использованием библиотеки oidc-connect. Это работает нормально, так что теперь у меня есть приложение Javascript, которое позволяет пользователю войти через мой экземпляр IS4. Это приложение JS представлено в IS4 как клиент, использующий неявный поток согласно инструкциям.

Теперь я хочу интегрировать свой экземпляр IS4 с моим магазином реальных пользователей. После прочтения нескольких статей мне кажется, что мне нужно предоставить IResourceOwnerPasswordValidator а также IProfileService согласно этому так посту и различным другим.

Я сделал это, сейчас просто используя фиктивный репозиторий пользователей, который работает с фиктивным набором пользователей в памяти, а не с реальным внешним хранилищем. Моя инициализация 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. Я ввожу учетные данные, которые я ожидаю, но неверное имя пользователя или пароль возвращается. Отладка Я вижу, что код никогда не входит в мой ValidateAsync метод в CustomResourceOwnerPasswordValidator, Однако случайно я обнаружил, что если я ввожу либо bob, либо alice в качестве имени пользователя и пароля (например, при вводе bob в качестве имени пользователя и пароля), то я аутентифицируюсь и могу вернуться к своему приложению. Это не имеет никакого смысла. Я уверен, что не случайно, что это имена пользователей, которые являются двумя пользователями по умолчанию, используемыми образцами пользователей в памяти IdentityServer4, но вы можете видеть выше, я больше не использую пользователей в памяти. На самом деле, это все еще верно, даже когда я комментирую GetUsers метод из Config.cs,

Я не могу этого объяснить, но в любом случае я хочу использовать мой пользовательский магазин. Поэтому я прочитал немного дальше и увидел предложения, которые я могу использовать только IResourceOwnerPasswordValidator с клиентами, которые имеют GrantTypes.ResourceOwnerPassword, Однако, когда я изменяю клиент моего приложения Javascript в IS4, чтобы использовать этот тип предоставления, при попытке войти в систему я вижу ошибку unauthorized_client.

Так что я застрял. Я думал, что, поскольку у меня есть настройки, работающие с пользователями в оперативной памяти, использование реального пользовательского магазина не составит большого труда, но, очевидно, что-то мне не хватает. Ваше руководство будет высоко ценится.

1 ответ

Решение

Вам не нужно вводить свой ProfileService как зависимость в ваших услугах.

Вам нужно что-то вроде:

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

А кроме того - ваш CustomProfileService не нужно реализовывать IProfileService (метод принимает универсальный).

Настоящая "магия" происходит в AccountController в:

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

Там, в LoginInputModel у вас есть имя пользователя, пароль и т. д., что вам нужно проверить, поэтому в конце нет реальной необходимости IProfileService если вы пишете свой собственный пользовательский менеджмент.

Другие вопросы по тегам