Создание пользователя с IdentityServer4 из нескольких API - PullRequest
0 голосов
/ 02 февраля 2019

Так что я некоторое время колотил головой из-за этой проблемы.

У нас есть одно веб-приложение, которое использует IdentityServer4 и AspNetIdentity для аутентификации и регистрации пользователей ( это работает, как предполагалось ).Кроме того, у нас есть другой API (внутри того же решения) , который может использовать IdentityServer4 для аутентификации пользователей, обращающихся к API .

Однако проблема в том, что кроме аутентификации мы не можем использовать API для создания новых пользователей .

Например, пользователи должны иметь возможность создавать других пользователей через веб-API, а не только из веб-приложения, поскольку в нашем случае пользователи связаны с другими пользователями (представьте, что это несколько профилей).

Я не очень знаком со всеми службами конфигурации, которые предлагают .Net Core Framework, и я пробовал несколько способов доступа к диспетчеру пользователей веб-приложения через API, чтобы зарегистрировать своих пользователей с помощью классических запросов POST, нокажется, ничего не работает.Поиск в Интернете сложен, потому что наша проблема довольно специфична, поэтому я публикую здесь.

API Startup.cs - ConfigureServices:

services.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme)
.AddIdentityServerAuthentication(options =>
{
     // base-address of your identityserver
     options.Authority = Configuration["IdentityServer:Url"];

     // name of the API resource
     options.ApiName = Configuration["IdentityServer:APIName"];
     options.ApiSecret = Configuration["IdentityServer:APISecret"];

     options.EnableCaching = true;
     options.CacheDuration = TimeSpan.FromMinutes(10); // that's the default

     options.RequireHttpsMetadata = Convert.ToBoolean(Configuration["IdentityServer:RequireHttpsMetadata"]);
});

API Startup.cs - Настройка:

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }

    app.UseCors("AllowAllOrigins");
    app.UseAuthentication();
    app.UseMvc();
}

API UsersController.cs - Конструктор:

private readonly UserManager<ApplicationUser> _userManager;
private readonly ApplicationDbContext _context;

public UsersController(IUserService service,
ApplicationDbContext context,
UserManager<ApplicationUser> userManager)
{
    _service = service;
    _userManager = userManager;
    _context = context;
}

Теперь проблема в том, что при запускеAPI и попытаться получить доступ к UsersController Я получаю следующую ошибку:

System.InvalidOperationException: Unable to resolve service for type 'Microsoft.AspNetCore.Identity.UserManager`1[XXXXX.Data.Models.ApplicationUser]' while attempting to activate 'XXXXXXX.Api.Controllers.UsersController'.

Я искренне надеюсь, что смогу найти хоть какой-нибудь совет о том, как с ним работать.

Пожалуйста, если что-тонеясный ответ, и я буду более чем рад добавить дополнительную информацию или прояснить ситуацию.

С уважением,

Мариос.

РЕДАКТИРОВАТЬ: Спасибо всем за ответ.Фрагмент кода, предоставленный @Vidmantas ниже, сделал свое дело.

Из-за моего ограниченного знания ядра .net я много проб и ошибок выполнял функцию настройки служб, которая, как вы можете себе представить, не работала.Я твердо верю, что использование ядра .net довольно просто (например, API), но когда дело доходит до конфигурирования сервисов, сложность (в основном озадачивающая / сбивающая с толку) взрывается.

Что касается архитектуры, вы дали мне хорошие идеи для будущего рефакторинга.Записки сделаны.

Мариос.

Ответы [ 2 ]

0 голосов
/ 04 февраля 2019

У меня такая же ситуация, как и у вас.

  • Сервер идентификации с пользователями asp .net.(БД содержит данные клиентов и пользователей)
  • API (база данных содержит доступ к данным приложения) .net Framework
  • Приложение .net Framework.

Наш пример использования былчто обычно новые пользователи будут созданы через сервер идентификации.Однако мы также хотели, чтобы приложение могло приглашать пользователей.Таким образом, я мог войти в приложение, и я хотел пригласить моего друга.Идея заключалась в том, что приглашение будет действовать так же, как если бы пользователь создавал себя.

Таким образом, он отправит электронное письмо моему другу с прикрепленным кодом, после чего пользователь сможет указать свой пароль и создать учетную запись.

Для этого я создал новое действие наКонтроллер моего аккаунта.

[HttpGet]
    [AllowAnonymous]
    public async Task<IActionResult> Invited([FromQuery] InviteUserRequest request)
    {

        if (request.Code == null)
        {
            RedirectToAction(nameof(Login));
        }
        var user = await _userManager.FindByIdAsync(request.UserId.ToString());
        if (user == null)
         {
          return View("Error");
        }

        var validateCode = await _userManager.VerifyUserTokenAsync(user, _userManager.Options.Tokens.PasswordResetTokenProvider, "ResetPassword", Uri.UnescapeDataString(request.Code));
        if (!validateCode)
        {
         return RedirectToAction(nameof(Login), new { message = ManageMessageId.PasswordResetFailedError, messageAttachment = "Invalid code." });
        }

        await _userManager.EnsureEmailConfirmedAsync(user);
        await _userManager.EnsureLegacyNotSetAsync(user);

        return View(new InvitedViewModel { Error = string.Empty, Email = user.Email, Code = request.Code, UserId = user.Id });
    }

Когда пользователь принимает электронное письмо, мы добавляем его.

[HttpPost]
    [AllowAnonymous]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> Invited([FromForm] InvitedViewModel model)
    {
        if (!ModelState.IsValid)
        {
            model.Error = "invalid model";
            return View(model);
        }

        if (!model.Password.Equals(model.ConfirmPassword))
        {

            model.Error = "Passwords must match";
            return View(model);
        }
        if (model.Terms != null && !model.Terms.All(t => t.Accept))
        {
            return View(model);
        }
        var user = await _userManager.FindByEmailAsync(model.Email);
        if (user == null)
        {             
            // Don't reveal that the user does not exist
            return RedirectToAction(nameof(Login), new { message = ManageMessageId.InvitedFailedError, messageAttachment = "User Not invited please invite user again." });
        }

        var result = await _userManager.ResetPasswordAsync(user, Uri.UnescapeDataString(model.Code), model.Password);

        if (result.Succeeded)
        {            
            return Redirect(_settings.Settings.XenaPath);
        }

        var errors = AddErrors(result);
                    return RedirectToAction(nameof(Login), new { message = ManageMessageId.InvitedFailedError, messageAttachment = errors });
    }

Причина, по которой это делается так, заключается в том, что только сервер идентификации должен читать и писать.в свою базу данных.API и сторонние приложения никогда не должны напрямую изменять базу данных, контролируемую другим приложением.таким образом, API сообщает серверу идентификации, чтобы он пригласил пользователя, а затем сервер управления сам контролирует все остальное.

Также, делая это таким образом, он устраняет необходимость иметь менеджера пользователей в вашем API :)

0 голосов
/ 02 февраля 2019

Если я вас правильно понимаю, то на самом деле вы не должны создавать пользователей с помощью API - вот почему у вас есть Identity Server 4 - чтобы предоставить центральные полномочия для аутентификации для вашей пользовательской базы.Что вам действительно нужно:

  • набор конечных точек API на стороне Identity Server 4 для управления совершенно новым API AspNetIdentity
  • , но с той же базой данных, что и Identity Server 4, для вашегоAspNetIdentity
  • попросит вашего API совместно использовать базу данных для AspNet Identity

Если вы выберете последний вариант, вам, вероятно, понадобится что-то вроде ниже, чтобы добавить:

services.AddDbContext<IdentityContext>(); //make sure it's same database as IdentityServer4

services.AddIdentityCore<ApplicationUser>(options => { });
new IdentityBuilder(typeof(ApplicationUser), typeof(IdentityRole), services)
    .AddRoleManager<RoleManager<IdentityRole>>()
    .AddSignInManager<SignInManager<ApplicationUser>>()
    .AddEntityFrameworkStores<IdentityContext>();

Это даст вам достаточно услуг для использования UserManager и не будет устанавливать никаких ненужных схем аутентификации.

Я бы не рекомендовал последний подход из-за разделения проблем - ваш API должен бытьзаботится о предоставлении ресурсов, а не о создании пользователей и предоставлении ресурсов.Первый и второй подход хороши, на мой взгляд, но я бы всегда использовал чистый отдельный сервис для управления AspNetIdentity.

Пример архитектуры одного из моих проектов, где мы реализовали такой подход:

  • auth.somedomain.com - веб-приложение IdentityServer4 с AspNetIdentity для аутентификации пользователя.
  • accounts.somedomain.com - веб-приложение AspNetCore с AspNetIdentity (та же база данных, что и Identity Server 4) для управления пользователями AspNetIdentity
  • webapp1.somedomain.com - веб-приложение, в котором находится вся ваша логика интерфейса (может, конечно, иметь и бэкенд, если AspNetCore MVC или что-то в этом роде)
  • api1.somedomain.com - веб-приложение исключительнодля целей API (если вы используете одно приложение для внешнего интерфейса и внутреннего интерфейса, то вы можете объединить два последних)
...