2

I'm developing two different applications, I will name them A and B.

  • A is an internet platform, where you can logon only if you have a valid user account.
  • B is an intranet platform, where users can authenticate via Active Directory. An administrator using application B should be able to create new user accounts for application A.

After the creation of a new user account, I want to be able to realize different functions, for example to send an e-mail to the registered mail address, so the new user can change the default password.

All the functionalities that I want to implement, can be done by the UserManager (see section "Use another app to add users" in the following link: https://learn.microsoft.com/en-us/aspnet/core/security/authentication/scaffold-identity?view=aspnetcore-3.1&tabs=visual-studio#disable-register-page).

Based on this I implemented the following code:

public class ControllerClass : Controller
{
    private readonly HelperClass _helper;
    private readonly UserManager<IdentityUser> _userManager;

    public ControllerClass (UserManager<IdentityUser> userManager)
    {
        _userManager = userManager;
        _helper= new HelperClass (userManager);
    }
}

public class HelperClass 
{
    private readonly DbContext _db;
    private readonly UserManager<IdentityUser> _userManager;

    public HelperClass (UserManager<IdentityUser> userManager)
    {
        _db = new DbContext ();
        _userManager = userManager;
    }

    private async Task<string> EnsureUser(string userName, string userPassword)
    {
        var user = await _userManager.FindByNameAsync(userName);

        if (user == null)
        {
            user = new IdentityUser()
            {
                UserName = userName                    
            };
            await _userManager.CreateAsync(user, userPassword);
        }

        return user.Id;
    }


    internal async void CreateUser(UserVM uvm, int id)
    {
        var userId = await EnsureUser(uvm.userName, uvm.userPassword);

        // TODO ...
    }

}

Unfortunately I didn't manage to include the UserManager into my application B. I got the following error message: "An unhandled exception occurred while processing the request. InvalidOperationException: Unable to resolve service for type 'Microsoft.AspNetCore.Identity.UserManager`1[IdentityUser]' while attempting to activate 'ControllerClass '."

Do you have an idea, how I can add the UserManager to manage the users for another application?

schmityv
  • 68
  • 1
  • 6

2 Answers2

2

Well, the specific error you're getting is simply because UserManager<TUser> is not registered in the service collection. In order to inject anything, you must first register it. In your actual user-facing app, that's being done by services.AddIdentity(). However, that does a lot more than just register UserManager<TUser>, so you shouldn't just run off and add the same command to your second app.

You could add a registration for it specifically:

services.AddScoped<UserManager<ApplicationUser>>();

However, it actually has a ton of dependencies, each of which would also need to be registered. If you look at the constructor, there's seven services not registered out of the box, many of which have their own dependency trees. Long and short, it's a pain. There's also the matter of separation of concerns, here. This would require adding in the whole data layer from the other app.

Your best bet is to simply expose an API on the Identity app (and lock it down, of course). That way, all the logic of working with users stays with the rest of that logic. The administration app, then, can call out to the API to add, update, delete, etc. users without having to have knowledge of how that's actually done.

Chris Pratt
  • 232,153
  • 36
  • 385
  • 444
  • 1
    Thank you very much, that makes a lot of sense. In the current solution I'm adding (or editing) the user account manually via the entity framework. Now I'm thinking about moving everything to an API following your answer. I hoped it would be easier to solve the problem. – schmityv Apr 08 '20 at 06:48
  • I'm working together with schmityv: we figured out that we have already services.AddDefaultIdentity in the startup class of Appl B. So if we change in the constructor from UserManager to the standard UserManager and make "OurIdentityClass" inhereting from "IdentityUser" the injection works as intended. However the userManager is not working (has no users inside) and tasks for finding users just time out... any input on that? – misanthrop Apr 08 '20 at 08:29
  • Probably a connection string issue. Not hitting the right or any database. – Chris Pratt Apr 08 '20 at 13:02
1

Answering after 2 years. For future reader, You can use

 services.AddIdentityCore<IdentityUser>();

which adds necessary services that are for user-management add/delete etc. without adding Login service.

to add EntityFramework you can create context and use like this

 services.AddDbContext<ApplicationAuthDbContext>(options =>
 {
    // Configure the context to use postgresql.
    options.UseNpgsql(config.GetConnectionString("AuthDbContext"))
     .UseSnakeCaseNamingConvention();
 });
 
services.AddIdentityCore<IdentityUser>()
   .AddEntityFrameworkStores<ApplicationAuthDbContext>();

For more information

https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.dependencyinjection.identityservicecollectionextensions.addidentitycore?view=aspnetcore-6.0

Ashwath NM
  • 11
  • 3