Search code examples
c#asp.net-coreasp.net-identityidentityserver4

User creation with IdentityServer4 from multiple API's


So I have been bashing my head for a while with this problem.

We have one web app that is using IdentityServer4 and AspNetIdentity to authenticate and register users (this is working as intended). In addition, we have an other API (inside the same solution) that is able to use IdentityServer4 to authenticate users accessing the API.

However, the problem is, that besides authentication we cannot use the API to create new users.

For instance, users should be able to create other users through the web API and not only from the web app, because in our case, users are linked to other users (think of it as multiple profiles).

I am not really familiar with all the configuration services that come up with .Net Core framework and I have tried multiple ways of accessing the user manager of the web app through the API to register my users through classic POST requests but nothing seems to be working. Searching online is tricky because our problem is kind of very specific, that's why I am posting here.

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 - Configure:

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

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

API UsersController.cs - Constructor:

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

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

Now the problem is that when I start the API and try to access the UsersController I get the following error:

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'.

I sincerely hope I can find at least some advice on how to proceed with it.

Please if something is unclear reply and I will be more than happy to add more information or make things clear.

Kind regards,

Marios.

EDIT: Thanks all for replying. The code snippet provided below by @Vidmantas did the trick.

Due to my limited knowledge of .net core I did a lot of trial and error in the configure services function which, as you can imagine, didn't work. I strongly believe that using .net core is kind of easy (e.g. API), but when it comes to configuring services the complexity (puzzling/confusing mostly) explodes.

As for the architecture, you gave me good ideas for future refactoring. Notes taken.

Marios.


Solution

  • If I understand you correctly, then you are not really supposed to create users through the API - that is why you have Identity Server 4 in place - to provide central authority for authentication for your user base. What you actually need:

    • a set of API endpoints on the Identity Server 4 side to manage AspNetIdentity
    • completely new API but one that shares the same database with Identity Server 4 for your AspNetIdentity
    • have your API share the database for AspNet Identity

    If you go with the last option then you probably need something like below to add the:

    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>();
    

    This will give you enough services to use the UserManager and it won't set up any unnecessary authentication schemes.

    I would not recommend the last approach due to the separation of concerns - your API should be concerned about providing resources, not creating users and providing resources. First and second approach are alright in my opinion, but I would always lean for clean separate service for AspNetIdentity management.

    An example architecture from one of my projects where we implemented such approach:

    • auth.somedomain.com - IdentityServer4 web app with AspNetIdentity for user authentication.
    • accounts.somedomain.com - AspNetCore web app with AspNetIdentity (same database as Identity Server 4) for AspNetIdentity user management
    • webapp1.somedomain.com - a web app where all your front end logic resides (can ofcourse have a backend as well if AspNetCore MVC or something like that)
    • api1.somedomain.com - a web app purely for API purposes (if you go single app for front end and backend then you can combine the last two)