48

I'm using Unity for Dependencies Injection and using Identiy Provider to manage the user login, register, email confirmation, etc.

When I try to register a user, I have this problem:

The current type, Microsoft.Owin.Security.IAuthenticationManager, is an interface and cannot be constructed. Are you missing a type mapping?

I have no idea how to register this Interface (IAuthenticationManager) in my Unity container.

I tried registering the interface with this code, but if I put it, I have other problem:

No IUserTokenProvider is registered.

 container.RegisterType<HttpContextBase>(
            new InjectionFactory(_ => new HttpContextWrapper(HttpContext.Current)));
        container.RegisterType<IOwinContext>(new InjectionFactory(c => c.Resolve<HttpContextBase>().GetOwinContext()));
        container.RegisterType<IAuthenticationManager>(
            new InjectionFactory(c => c.Resolve<IOwinContext>().Authentication));

I put some code of the app (If I don't use Unity, all work fine):

AccountController

private IAuthenticationManager AuthenticationManager
        {
            get
            {
                return HttpContext.GetOwinContext().Authentication;
            }
        }

IdentityConfig.cs

public class ApplicationUserManager : UserManager<ApplicationUser>
    {
        public ApplicationUserManager(IUserStore<ApplicationUser> store)
            : base(store)
        {

        }

        public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options,
            IOwinContext context)
        {
            var manager = new ApplicationUserManager(new UserStore<ApplicationUser>(context.Get<ApplicationDbContext>()));
            // Configure validation logic for usernames
            manager.UserValidator = new UserValidator<ApplicationUser>(manager)
            {
                AllowOnlyAlphanumericUserNames = false,
                RequireUniqueEmail = true
            };
            // Configure validation logic for passwords
            manager.PasswordValidator = new PasswordValidator
            {
                RequiredLength = 6,
                RequireNonLetterOrDigit = true,
                RequireDigit = true,
                RequireLowercase = true,
                RequireUppercase = true,
            };
            // Configure user lockout defaults
            manager.UserLockoutEnabledByDefault = true;
            manager.DefaultAccountLockoutTimeSpan = TimeSpan.FromMinutes(5);
            manager.MaxFailedAccessAttemptsBeforeLockout = 5;
            // Register two factor authentication providers. This application uses Phone and Emails as a step of receiving a code for verifying the user
            // You can write your own provider and plug in here.
            manager.RegisterTwoFactorProvider("PhoneCode", new PhoneNumberTokenProvider<ApplicationUser>
            {
                MessageFormat = "Your security code is: {0}"
            });
            manager.RegisterTwoFactorProvider("EmailCode", new EmailTokenProvider<ApplicationUser>
            {
                Subject = "SecurityCode",
                BodyFormat = "Your security code is {0}"
            });
            manager.EmailService = new EmailService();
            manager.SmsService = new SmsService();
            var dataProtectionProvider = options.DataProtectionProvider;
            if (dataProtectionProvider != null)
            {
                manager.UserTokenProvider =
                    new DataProtectorTokenProvider<ApplicationUser>(dataProtectionProvider.Create("ASP.NET Identity"));
            }
            return manager;
        }
    }

    // Configure the RoleManager used in the application. RoleManager is defined in the ASP.NET Identity core assembly
    public class ApplicationRoleManager : RoleManager<IdentityRole>
    {
        public ApplicationRoleManager(IRoleStore<IdentityRole, string> roleStore)
            : base(roleStore)
        {
        }

        public static ApplicationRoleManager Create(IdentityFactoryOptions<ApplicationRoleManager> options, IOwinContext context)
        {
            return new ApplicationRoleManager(new RoleStore<IdentityRole>(context.Get<ApplicationDbContext>()));
        }
    }

    public class EmailService : IIdentityMessageService
    {
        public Task SendAsync(IdentityMessage message)
        {
            // Plug in your email service here to send an email.
            return Task.FromResult(0);
        }
    }

    public class SmsService : IIdentityMessageService
    {
        public Task SendAsync(IdentityMessage message)
        {
            // Plug in your sms service here to send a text message.
            return Task.FromResult(0);
        }
    }

    // This is useful if you do not want to tear down the database each time you run the application.
    // public class ApplicationDbInitializer : DropCreateDatabaseAlways<ApplicationDbContext>
    // This example shows you how to create a new database if the Model changes
    public class ApplicationDbInitializer : DropCreateDatabaseIfModelChanges<ApplicationDbContext>
    {
        protected override void Seed(ApplicationDbContext context)
        {
            InitializeIdentityForEF(context);
            base.Seed(context);
        }

        //Create User=Admin@Admin.com with password=Admin@123456 in the Admin role        
        public static void InitializeIdentityForEF(ApplicationDbContext db)
        {
            var userManager = HttpContext.Current.GetOwinContext().GetUserManager<ApplicationUserManager>();
            var roleManager = HttpContext.Current.GetOwinContext().Get<ApplicationRoleManager>();
            const string name = "admin@example.com";
            const string password = "Admin@123456";
            const string roleName = "Admin";

            //Create Role Admin if it does not exist
            var role = roleManager.FindByName(roleName);
            if (role == null)
            {
                role = new IdentityRole(roleName);
                var roleresult = roleManager.Create(role);
            }

            var user = userManager.FindByName(name);
            if (user == null)
            {
                user = new ApplicationUser { UserName = name, Email = name };
                var result = userManager.Create(user, password);
                result = userManager.SetLockoutEnabled(user.Id, false);
            }

            // Add user admin to Role Admin if not already added
            var rolesForUser = userManager.GetRoles(user.Id);
            if (!rolesForUser.Contains(role.Name))
            {
                var result = userManager.AddToRole(user.Id, role.Name);
            }
        }
    }

    public class ApplicationSignInManager : SignInManager<ApplicationUser, string>
    {
        public ApplicationSignInManager(ApplicationUserManager userManager, IAuthenticationManager authenticationManager)
            :
                base(userManager, authenticationManager)
        {



        }

        public override Task<ClaimsIdentity> CreateUserIdentityAsync(ApplicationUser user)
        {
            return user.GenerateUserIdentityAsync((ApplicationUserManager)UserManager);
        }

        public static ApplicationSignInManager Create(IdentityFactoryOptions<ApplicationSignInManager> options, IOwinContext context)
        {
            return new ApplicationSignInManager(context.GetUserManager<ApplicationUserManager>(), context.Authentication);
        }
    }

Thanks!!

chemitaxis
  • 13,889
  • 17
  • 74
  • 125
  • I followed this tutorial... http://www.asp.net/identity/overview/features-api/two-factor-authentication-using-sms-and-email-with-aspnet-identity – chemitaxis Jul 14 '14 at 09:00
  • 1
    I did too @chemitaxis and it turned out to use an anti-pattern with this nonsense of 'CreatePerOwinContext' which disconnects you from the intentions of your DI container. I'm now trying to escape it's clutches. app.CreatePerOwinContext(ApplicationDbContext.Create); app.CreatePerOwinContext(ApplicationUserManager.Create); app.CreatePerOwinContext(ApplicationRoleManager.Create); app.CreatePerOwinContext(ApplicationSignInManager.Create); – The Senator May 02 '18 at 16:46

6 Answers6

63

Here is what I did to make Unity play nice with ASP.NET Identity 2.0:

I added the following to the RegisterTypes method in the UnityConfig class:

container.RegisterType<DbContext, ApplicationDbContext>(
    new HierarchicalLifetimeManager());
container.RegisterType<UserManager<ApplicationUser>>(
    new HierarchicalLifetimeManager());
container.RegisterType<IUserStore<ApplicationUser>, UserStore<ApplicationUser>>(
    new HierarchicalLifetimeManager());

container.RegisterType<AccountController>(
    new InjectionConstructor());
Matthew Walton
  • 9,809
  • 3
  • 27
  • 36
user3849637
  • 662
  • 6
  • 3
  • 3
    This seems to be most complete answer I have found on SO. This indeed covers all of the mappings you need to make with unity when using ASP.net Identity 2. Just register all other controllers which require injection of Identity interfaces. – Steven Anderson Nov 27 '14 at 10:13
  • I agree with @StevenAnderson as I found this to be a succinct answer that worked for me as well. Once I got this working then I was able to address a CORS issue. – JQII Dec 30 '14 at 17:00
  • 3
    I need this line too: container.RegisterType(new InjectionConstructor()); – Jenan Sep 03 '15 at 17:04
  • 3
    Is it just me or does this answer not cover the IAuthenticationManager problem originally proposed by the OP? I already had these four types registered in my UnityConfig but stil get the problem the OP posted. – Xipooo Sep 29 '16 at 20:31
  • If you want to allow the managing of users, e.g. /Manage/ then you'll also `container.RegisterType(new InjectionConstructor());` as @Jenan has stated. I had the other 4 lines in this answer but also needed this additional line. – Percy Dec 06 '16 at 10:51
  • 2
    This is not a proper solution. It requires you to have a parameter-less ctor – hyankov Feb 09 '17 at 16:24
  • @HristoYankov the parameterless constructor is already there, both, in the AccountController and ManageController. It's part of the "Internet Application " project template. I see nothing wrong with them. – Felipe Romero Apr 20 '18 at 21:03
  • The problem in the original question is how to register an IAuthenticationManager. Which class is it that does that? IOwinContext>().Authentication is not an acceptable answer as that is magical. What is in there? – The Senator May 02 '18 at 16:48
  • Hey I know this is kind of old question, but could you please clarify a bit on why are we registering DbContext with HierarchicalLifeTimeManager, and not PerRequestLifetimeManager? – mi105 Nov 08 '18 at 12:50
48

Try adding below line in the class UnityConfig:

container.RegisterType<IAuthenticationManager>(
    new InjectionFactory(
        o => System.Web.HttpContext.Current.GetOwinContext().Authentication
    )
);
H. Pauwelyn
  • 13,575
  • 26
  • 81
  • 144
abdulbasit
  • 1,856
  • 15
  • 17
  • 1
    I marked this up as an answer a while ago. But I'm revisiting how this should work. Surely the IAuthenticationManager should be a concrete type (whatever that is?) we can register with Unity (or chosen DI container) and control it without the GetOwinContext() magic? – The Senator May 02 '18 at 16:36
5

If you really want to use Unity to manage all your dependencies, you could try to register also the IAuthenticationManager in Unity

    container.RegisterType<IAuthenticationManager>(
        new InjectionFactory(c => HttpContext.Current.GetOwinContext().Authentication));

With some small adaptations, you can then use Unity to resolve all needed dependencies for Asp.net Identity.

I found a great post on this (also tested by me) here:

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

Pinte Dani
  • 1,989
  • 3
  • 28
  • 35
5

This will also work as a complete configuration to allow the use of Unity with Identity 2.0:

container.RegisterType<MyDbContext>(new PerRequestLifetimeManager(), new InjectionConstructor());

// Identity
container.RegisterType<UserManager<User>>(new HierarchicalLifetimeManager());
container.RegisterType<SignInManager<User, string>>(new HierarchicalLifetimeManager());
container.RegisterType<IUserStore<User>, UserStore<User>>(new PerRequestLifetimeManager(), new InjectionFactory(x => new UserStore<User>(GetConfiguredContainer().Resolve<MyDbContext>())));
container.RegisterType<IAuthenticationManager>(new InjectionFactory(x => HttpContext.Current.GetOwinContext().Authentication));

With this setting, you won't need to register the AccountController or ManageController types with Unity.

Phillippe Santana
  • 2,906
  • 2
  • 28
  • 29
2

Reputation won't let me comment, but to add to user3849637s marked answer, for MVC with Update 3, you will also need to add the ManageController as some functions use that with the updated scaffolding:

container.RegisterType<ManageController>(new InjectionConstructor()); 
0
container.RegisterFactory<IAuthenticationManager>(o => System.Web.HttpContext.Current.GetOwinContext().Authentication);

This will answer the question posted by the OP and is an up to date response for anyone finding this via Google like myself. RegisterFactory is the new way of doing this in Unity.