Search code examples
asp.net-identityasp.net-identity-2identityserver3

How to register ApplicationUserManager with IdentityServer DI framework?


I am using IdentityServer3 for authentication. All users are stored in Sql DB so I am also using Microsoft.AspNet.Identity framework for actual authentication, and for the same purpose i have created my own ApplicationUserManager class.

The AspNet identity has IoC feature integrated into OWIN middleware. and it registers the ApplicationUserManager like:

 app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);  

it takes function delegate which returns a new instance of a ApplicationUserManager

public static ApplicationUserManager 
Create(IdentityFactoryOptions<ApplicationUserManager> options,
        IOwinContext context)
    {
        var manager = new ApplicationUserManager(new UserStore<ApplicationUser>(context.Get<ApplicationDbContext>()));

However, IdentityServer uses its own DI framework and (i think) we cannot use static Create() method to rgister ApplicationUserManager with IdentityServer, Also Create() method takes IdentityFactoryOptions and a IOwinContext as parameters.

I followed this SO post and i change the implementation of ApplicationUserManager to use Constructor Injection

public class ApplicationUserManager : UserManager<ApplicationUser, string>
{   
    public ApplicationUserManager(ApplicationUserStore store, IdentityFactoryOptions<ApplicationUserManager> options)
        : base(store)
    {          
        // Configure validation logic for usernames
        UserValidator = new UserValidator<ApplicationUser>(this)
        {
            AllowOnlyAlphanumericUserNames = false,
            RequireUniqueEmail = true
        };

        // Configure validation logic for passwords
        PasswordValidator = new PasswordValidator
        {
            RequiredLength = 6,
            RequireNonLetterOrDigit = true,
            RequireDigit = true,
            RequireLowercase = true,
            RequireUppercase = true,
        };

        // Configure user lockout defaults
        UserLockoutEnabledByDefault = true;
        DefaultAccountLockoutTimeSpan = TimeSpan.FromMinutes(5);
        MaxFailedAccessAttemptsBeforeLockout = 5;

        EmailService = new EmailService();
        SmsService = new SmsService();

        var dataProtectionProvider = options.DataProtectionProvider;
        if (dataProtectionProvider != null)
        {
            UserTokenProvider =
                new DataProtectorTokenProvider<ApplicationUser>(dataProtectionProvider.Create("ASP.NET Identity"));
        }
    }        
}

And then register all the services with IdentityServer's own DI framework as below

factory.UserService = new Registration<IUserService, UserService>();
factory.Register(new Registration<ApplicationUserManager>());
factory.Register(new Registration<ApplicationUserStore>());
factory.Register(new Registration<IdentityFactoryOptions<ApplicationUserManager>>(resolver => new IdentityFactoryOptions<ApplicationUserManager>
            {
                DataProtectionProvider = new Microsoft.Owin.Security.DataProtection.DpapiDataProtectionProvider("ASP.NET Identity")
            }));
factory.Register(new Registration<ApplicationDbContext>(resolver => new ApplicationDbContext(ApplicationConfig.ConnectionString)));

Questions

  1. Is this a correct way of registering ApplicationUserManager with IdentiServer's DI framework?
  2. I am creating DataProtectionProvider during the registration and the UserTokenProvider inside the constructor. How this 2 providers are being used by IdentityServer?
  3. Note that im not registering IOwinContext anywhere since ApplicationUserManager's constructor does not need it anymore, will that cause any issue down the OWIN pipeline?.
  4. What would be ideal registration mode for ApplicationUserManager?

Solution

  • These two articles helped to solve my issue

    http://tech.trailmax.info/2014/06/asp-net-identity-and-cryptographicexception-when-running-your-site-on-microsoft-azure-web-sites/

    http://tech.trailmax.info/2014/09/aspnet-identity-and-ioc-container-registration/

    However my ApplicationUserManager is in separate class library and startup.cs is in web project. The class library does not have reference to the web project. So i refactored ApplicationUserManager Constructor

        public ApplicationUserManager(ApplicationUserStore store, IDataProtectionProvider dataProtectionProvider)
            : base(store)
        {
           // other stuff
    
    
            if (dataProtectionProvider != null)
            {
                UserTokenProvider =
                    new DataProtectorTokenProvider<ApplicationUser>(dataProtectionProvider.Create("UserToken"));
            }
        }
    

    and also registered IDataProtectionProvider with DI framework. I am not using Unity as IoC. I am using IdentityServer's own DI framework. So i register IDataProtectionProvider as

      factory.Register(new Registration<IDataProtectionProvider>(resolver => Startup.DataProtectionProvider));