19

I am trying to figure out how to inject UserManager and SignInManager. I have installed Ninject in my application and I am using it in the following manner:

Please consider this to be a brand new project. Inside Startup.cs I have the following:

public partial class Startup
{
    public void Configuration(IAppBuilder app)
    {
        ConfigureAuth(app);

        app.UseNinjectMiddleware(CreateKernel);
    }

    private static IKernel CreateKernel()
    {
        var kernel = new StandardKernel();
        kernel.Load(Assembly.GetExecutingAssembly());


        return kernel;
    }
}

now if I were to create some Dummy class and try to inject it based on its interface that works. I have tested it. What I am trying to figure out is how would I now strip out the following out of Startup.Auth.cs and inject it. Having no interfaces I can rely on, I am not sure how this is done:

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

Just to clarify one more time, my question is: How do I instantiate ApplicationUserManager and ApplicationSignInManager and inject it in my controller parameters. Here is the controller that I am trying to inject this into:

public AccountController(ApplicationUserManager userManager, ApplicationSignInManager signInManager)
{
    UserManager = userManager;
    SignInManager = signInManager;
}

EDIT:

Here is what I tried:

private static IKernel CreateKernel()
{
    var kernel = new StandardKernel();
    kernel.Load(Assembly.GetExecutingAssembly());

    kernel.Bind<IUserStore<ApplicationUser>>().To<UserStore<ApplicationUser>>();
    kernel.Bind<UserManager<ApplicationUser>>().ToSelf();

    return kernel;
}

But with this I get null reference error

Bagzli
  • 6,254
  • 17
  • 80
  • 163
  • You should take a look at the following hands-on lab [ASP.NET MVC 4 Dependency Injection](http://www.asp.net/mvc/overview/older-versions/hands-on-labs/aspnet-mvc-4-dependency-injection) – Nkosi Mar 26 '16 at 20:08
  • @Nkosi i have just gone through the article you have given me. This uses unity and I have gone through a similar guide like this before and they confuse the jesus out of me. I'm looking for a very simple example that is straight forward to my question. Do you know any I can look at? I've spend a week now on trying to make heads and tails out of all of this. Would really appreciate a straightforward answer at this point. – Bagzli Mar 26 '16 at 20:39
  • Have you tried `kernel.Bind().ToMethod(ctx => ApplicationUserManager.Create()).InRequestScope()` (etc.)? – BatteryBackupUnit Mar 26 '16 at 20:49
  • @BatteryBackupUnit but how do I accommodate for the fact that userManager takes SignInManager as one of its parameters? I am trying to avoid to create 2 sepparate instance of SignInManager – Bagzli Mar 26 '16 at 21:06
  • 2
    There are walkthroughs for configuring the MVC5 template for DI [here](http://tech.trailmax.info/2014/09/aspnet-identity-and-ioc-container-registration/) and [here](https://www.talksharp.com/configuring-autofac-to-work-with-the-aspnet-identity-framework-in-mvc-5). The latter one is better in my opinion, although it left out the changes required to the `ManageController`. Once you refactor the stock classes and controllers to use constructor injection, dropping a different DI container in is pretty straightforward. – NightOwl888 Mar 26 '16 at 21:22
  • @NightOwl888 thank you, that first article is what i have been staring at all week. The second one uses Autofac, but it does a good job explaining things. can you tell me what exactly needs to be done with in ManageController? – Bagzli Mar 26 '16 at 21:34
  • @NightOwl888 also, that second article uses ContainerBuilder, what would be equivalent for Ninject? Is it kernel? – Bagzli Mar 26 '16 at 21:37
  • 2
    [Here is a Gist](https://gist.github.com/NightOwl888/693879b0e19fa4210b43) with the changes to the project (ignore the changes to Startup.cs for now). I would suggest opening an issue on [Ninject.Web.Common](https://github.com/ninject/Ninject.Web.Common) for instructions, as the documentation is very unclear what you are supposed to do for OWIN MVC 5 integration. I tried to work it out, but Ninject is doing things in a strange way that requires a bootstrapper, where OWIN also already has one (and passes its configuration in). Another option - you could always change to a different container. – NightOwl888 Mar 27 '16 at 07:21
  • @NightOwl888 I have gone through your Gist, and everything there seems to be what every article is saying should be done. The only parts that are missing are the ones I am confused about lol. The injection part itself. The syntax to create UserManager and SignInManager to be injected to the controller. – Bagzli Mar 27 '16 at 16:24
  • @Bagzli Do you use WebApi? If so, it seems that you forgot to call `UseNinjectWebApi` method [setting up OWIN WebApi application](https://github.com/ninject/Ninject.Web.Common/wiki/Setting-up-a-OWIN-WebApi-application) – Old Fox Mar 29 '16 at 09:09
  • @OldFox I don't think I am. If I try to inject a class of my own that is based on interface, this setup works without problem. However I can't inject UserManager and SignInManager partially because they don't have interface and partially because I do not understand how to properly instantiate those with Ninject. – Bagzli Mar 29 '16 at 14:46
  • MVC5 and Wep.API uses two different stacks (different request lifecycle, DI container, etc.). And only Web.API is based on OWIN. So when you use `UseNinjectMiddleware` it will only work with ApiControllers. If you want to make it work with regular controllers you need to use the `NinjectWebCommon` class `RegisterServices` method which is added to your project if you are using the Ninject.MVC5 package. – nemesv Mar 29 '16 at 15:01
  • @nemesv do you have any examples of how all of this works with UserManager? – Bagzli Mar 29 '16 at 15:48
  • @Bagzli - I have done this before with Autofac (another DI implementation). I resolved it by creating my own proxy to the UserManager in my own interface and registering that with the DI framework. If that is sufficient I will include an answer and some code on how to do this. – Igor Mar 29 '16 at 17:46
  • @Igor there is an autofac guide here: https://www.talksharp.com/configuring-autofac-to-work-with-the-aspnet-identity-framework-in-mvc-5 but I am having problems translating it to Ninject as ninject doesn't do it with a container builder like autofac does and that really throws me off. I appreciate the offer though! – Bagzli Mar 29 '16 at 19:34
  • @Bagzli - Sorry, I was not clear. I meant I could write an example on how to do that with NInject but using a proxy that you use to access the usermanager and signinmanager. You then inject that proxy and access those through there instead of injecting those types directly. If you think that will solve your problem let me know, Ill provide some code. – Igor Mar 29 '16 at 19:37
  • @Igor So you are saying you would create a containter class (proxy) which would hold the two objects and then you inject the container class into the controller? I am not sure what setbacks that might introduce, can you think of any? – Bagzli Mar 29 '16 at 19:43
  • @Igor I actually found couple articles this morning that I haven't tested yet. I do not know if what they are saying here would work, won't be able to test till late tonight: http://stackoverflow.com/questions/23968065/ninject-usermanager-and-userstore and http://stackoverflow.com/questions/29089877/how-do-i-inject-identity-classes-with-ninject?rq=1 – Bagzli Mar 29 '16 at 19:44
  • @Bagzli - no setbacks, this is how I have implemented it (with success) in the past. The proxy can have an interface and you can also expose these classes directly or just have a limited number of pass through methods (or aggregate methods) that deal directly with the UserManager and SignInManager. It will allow for better abstraction in the long run. Those links look like exactly like what you need. – Igor Mar 29 '16 at 19:46
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/107665/discussion-between-bagzli-and-igor). – Bagzli Mar 29 '16 at 20:04

2 Answers2

24

Prerequisites

Start a new MVC5 application using the MVC template. This will install all the necessary dependencies as well as deploy the Startup.Auth.cs file which contains the bootstrap code for Microsoft.AspNet.Identity (it als includes the all the references for Microsoft.AspNet.Identity).

Install the following packages and update them to the latest afterwards.

Install-Package Ninject
Install-Package Ninject.MVC5

Configuration

Remove the default constructor on the AccountController so only the parameterized constructor remains. It should have the follownig signature.

public AccountController(ApplicationUserManager userManager, ApplicationSignInManager signInManager)

This will ensure that you will get an error if injection fails which is what we want.

NInject Configuration

The NInject NuGet package deployment will have created a file named NinjectWebCommon.cs where the boiler plate NInject registration takes place. This has a method with the following signature which you can extend with your registrations.

private static void RegisterServices(IKernel kernel)

Now we will add the following code to this method to get NInject automatically inject the ApplicationSignInManager and ApplicationUserManager instances.

private static void RegisterServices(IKernel kernel) {
    kernel.Bind<IUserStore<ApplicationUser>>().To<UserStore<ApplicationUser>>();
    kernel.Bind<UserManager<ApplicationUser>>().ToSelf();

    kernel.Bind<HttpContextBase>().ToMethod(ctx => new HttpContextWrapper(HttpContext.Current)).InTransientScope();

    kernel.Bind<ApplicationSignInManager>().ToMethod((context)=>
    {
        var cbase = new HttpContextWrapper(HttpContext.Current);
        return cbase.GetOwinContext().Get<ApplicationSignInManager>();
    });

    kernel.Bind<ApplicationUserManager>().ToSelf();
}

Thats it. Now you should be able to navigate to the Login or Register links and injection will occur.

Alternate Approach

I prefer a Proxy approach that exposes limited functionality for the ApplicationSignInManager and the ApplicationUserManager instances. I then inject this proxy into the necessary controllers. It helps abstract some of the Identity information away from the controllers which makes it easier to change in the future. This is not a new concept by any means and whether you do this or not depends on the size and complexity of your project as well as how you want to handle dependencies. So the benefits are (common actually for any proxy):

  • You can abstract some of the dependencies away from your code
  • You can streamline some of the calls within the api
  • You can expose only that functionality that you want to use including configurable parts
  • Change management should be easier if the interfaces ever change, now you change the calls in your proxy instead of all the calling code across your controllers.

Code example:

public interface IAuthManager
{
    Task<SignInStatus> PasswordSignInAsync(string userName, string password, bool rememberMe);
}

public class AuthManager : IAuthManager
{
    private ApplicationUserManager _userManager;
    ApplicationSignInManager _signInManager;

    public AuthManager(ApplicationUserManager userManager, ApplicationSignInManager signInManager)
    {
        this._userManager = userManager;
        this._signInManager = signInManager;
    }

    public Task<SignInStatus> PasswordSignInAsync(string userName, string password, bool rememberMe)
    {
        return _signInManager.PasswordSignInAsync(userName, password, rememberMe, true);
    }
}

Add the following line in your NInject dependency registration.

kernel.Bind<IAuthManager>().To<AuthManager>();

Alter your AccountController constructor to take in an instance of IAuthManager. Finally change your methods to refer to this proxy instead of the ASP.NET Identity classes directly.

Disclaimer - I did not wire up a complex call, just a very simple one to illustrate my point. This is also entirely optional and whether you do it or not really should depend on the scope and size of your project and how you plan on using the ASP.NET Identity framework

Igor
  • 60,821
  • 10
  • 100
  • 175
  • 1
    The alternate approach is the most important part. The service should be used to create a repo of sorts. Injecting such a service is no more valuable than instantiating it. Would you really replace it with another service that has the exact same exposed methods? – Dave Alperovich Mar 29 '16 at 22:57
  • 1
    @DaveAlperovich - I started a project not to long ago where I abstracted all the ASP.NET Identity libraries to its own isolated assembly (csproj) and exposed limited functionality with interfaces. This project is very large in scope and LoC so it made sense. I also have a couple of in house projects that are very limited in size and scope and took a couple of days to create, here I did no abstraction at all because it was not worth the time or effort. Although I prefer using a proxy/facade I believer whether you should depends on the ROI which is probably based on a number of factors. – Igor Mar 29 '16 at 23:51
  • 1
    I'd go one step further. It's not a popular opinion, but I don't believe there's any ROI in injecting services like UserManager. Wrap them, abstract them, create a repo with functional divisions. DI has become an end in itself, sadly. – Dave Alperovich Mar 30 '16 at 00:27
  • @Igor with your example, did you have to modify UserManager.Create method and move into constructor like I have? – Bagzli Mar 30 '16 at 14:26
  • @Bagzli - no. The only changes I had to make are the ones I listed above. If you need though I can zip my sample project and put it somewhere for you to download. – Igor Mar 30 '16 at 14:28
  • when i try to reset password it raise "No IUserTokenProvider is registered" exception. How can i inject IDataProtectionProvider to Ninject's CreateKernel method? – Yargicx Nov 06 '18 at 21:53
  • @Yargicx - Please do not ask new questions on old threads. Start your own question and include an [mcve]. See also [ask]. – Igor Nov 06 '18 at 22:11
20

To give an exact answer to what my question stated, here is the code and instructions:

Step 1: Create custom User Store

public class ApplicationUserStore : UserStore<ApplicationUser>
{
    public ApplicationUserStore(ApplicationDbContext context)
        : base(context)
    {
    }
}

Step 2: Update ApplicationUserManager and move code from Create method into constructor

public class ApplicationUserManager : UserManager<ApplicationUser>
{
    public ApplicationUserManager(IUserStore<ApplicationUser> store, IdentityFactoryOptions<ApplicationUserManager> options)
        : base(store)
    {
        this.UserValidator = new UserValidator<ApplicationUser>(this)
        {
            AllowOnlyAlphanumericUserNames = false,
            RequireUniqueEmail = true
        };

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

        // Configure user lockout defaults
        this.UserLockoutEnabledByDefault = true;
        this.DefaultAccountLockoutTimeSpan = TimeSpan.FromMinutes(5);
        this.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 it in here.
        this.RegisterTwoFactorProvider("Phone Code", new PhoneNumberTokenProvider<ApplicationUser>
        {
            MessageFormat = "Your security code is {0}"
        });
        this.RegisterTwoFactorProvider("Email Code", new EmailTokenProvider<ApplicationUser>
        {
            Subject = "Security Code",
            BodyFormat = "Your security code is {0}"
        });
        this.EmailService = new EmailService();
        this.SmsService = new SmsService();
        var dataProtectionProvider = options.DataProtectionProvider;
        if (dataProtectionProvider != null)
        {
            this.UserTokenProvider =
                new DataProtectorTokenProvider<ApplicationUser>(dataProtectionProvider.Create("ASP.NET Identity"));
        }
    }
}

Step 3: Modify the Startup.Auth class and comment out the following code

//app.CreatePerOwinContext(ApplicationDbContext.Create);
//app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
//app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);

Step 4: Update Account Controller (or any other controller in question) and add the following constructor

public AccountController(ApplicationUserManager userManager, ApplicationSignInManager signInManager, IAuthenticationManager authManager)
{
    _userManager = userManager;
    _signInManager = signInManager;
    _authManager = authManager;
}

Step 5: Update Account Controller and make properties only retrivable as so:

public ApplicationSignInManager SignInManager
{
    get
    {
        return _signInManager;
    }
}

public ApplicationUserManager UserManager
{
    get
    {
        return _userManager;
    }
}

private IAuthenticationManager AuthenticationManager
{
    get
    {
        return _authManager;
    }
}

Step 6: Update Startup.cs

public partial class Startup
{
    private IAppBuilder _app;
    public void Configuration(IAppBuilder app)
    {
        ConfigureAuth(app);
        _app = app;
        app.UseNinjectMiddleware(CreateKernel);
    }

    private IKernel CreateKernel()
    {
        var kernel = new StandardKernel();
        kernel.Load(Assembly.GetExecutingAssembly());

        kernel.Bind<ApplicationDbContext>().ToSelf().InRequestScope();
        kernel.Bind<IUserStore<ApplicationUser>>().To<ApplicationUserStore>();
        kernel.Bind<ApplicationUserManager>().ToSelf();
        kernel.Bind<ApplicationSignInManager>().ToSelf();
        kernel.Bind<IAuthenticationManager>().ToMethod(x => HttpContext.Current.GetOwinContext().Authentication);
        kernel.Bind<IDataProtectionProvider>().ToMethod(x => _app.GetDataProtectionProvider());

        return kernel;
    }
}

To further expand the answer to this question, based on the comments I have received:

These managers should not be injected as classes as then you are not accomplishing DI. What should be done instead is create multiple interfaces that further separate and group methods of UserManager according to your needs. Here is an example:

public interface IUserManagerSegment
{
    Task<IdentityResult> CreateAsync(ApplicationUser user, string password);
    Task<IdentityResult> CreateAsync(ApplicationUser user);
    Task<IdentityResult> ConfirmEmailAsync(string userId, string token);
    Task<ApplicationUser> FindByNameAsync(string userName);
    Task<bool> IsEmailConfirmedAsync(string userId);
    Task<IdentityResult> ResetPasswordAsync(string userId, string token, string newPassword);
    Task<IList<string>> GetValidTwoFactorProvidersAsync(string userId);
    Task<IdentityResult> AddLoginAsync(string userId, UserLoginInfo login);
    void Dispose(bool disposing);
    void Dispose();
}

The above method has a list of few random methods I chose just to illustrate the point. With this said, we would now inject the method based on the interface such as this:

kernel.Bind<IUserManagerSegment>().To<ApplicationUserManager>();

And now our AccountController constructor would look like this:

public AccountController(IUserManagerSegment userManager, ApplicationSignInManager signInManager, IAuthenticationManager authManager)  
{
    _userManager = userManager;
    _signInManager = signInManager;
    _authManager = authManager;
}

Same thing should be done to SignInManager and AuthenticationManager.

The code above has been tested and is working. Just ensure you have referenced the following DLLs:

Ninject.dll
Ninject.Web.Common
Ninject.Web.Common.OwinHost
Ninject.Web.Mvc

Bagzli
  • 6,254
  • 17
  • 80
  • 163
  • 2
    Nice, saw you posted this right as I was posting my solution. I tested everything with a new setup using VS 2015. Let me know if you still have any questions or need to see anything else? – Igor Mar 29 '16 at 22:48
  • 1
    Take a look at @Igor 's answer. TBH, while you have achieved something technically here, injecting the UserManager misses the point. DI is meant to de-couple, so you can replace one service with another. But would you ever replace this UserManager with another? It would have to expose the exact same methods. Better to create a User Repo with methods like `findUser(userid)`, `ChangePasswordUser(userid, newpswd)`, etc. This kind of repository is worth Injecting... and can be replaced in a worthwhile manner – Dave Alperovich Mar 29 '16 at 23:03
  • @DaveAlperovich are you talking about creating an interface that would show methods I intend on using and then extending my UserManager with that interface? – Bagzli Mar 30 '16 at 14:11
  • 1
    Yes. If you Inject against an interface that exposes every method on the UserManager, you haven't achieved much. If you wanted to replace the service because you moved to a different Authentication Provider, or new version, your code would still change unless all methods remained the same. That's why you want to inject more chunk services with a simpler Interface. For that, you want to wrap your UserManager with a UserRepo that does fewer things, and the logic is nested. – Dave Alperovich Mar 30 '16 at 14:16
  • 1
    @DaveAlperovich - agreed. Bagzil - my example above with the interface is very weak in that it was more designed to illustrate how to inject it and get it's implementation to still have access to the signinmanager and usermanager. You actual implementation should more closely mirror a [Facade pattern](https://en.wikipedia.org/wiki/Facade_pattern) where you abstract away the details of the ASP.NET Identity Framework and expose your own limited simplified interface where the wiring and details are hidden away. – Igor Mar 30 '16 at 14:45
  • @Igor I will spend this weekend writing up a more complex example and post it here just to make sure I understand what you guys are telling me. – Bagzli Mar 30 '16 at 15:21
  • @Igor I am marking my answer as correct due to completely answering what I was asking for initially, and that was how to inject UserManager and SignInManager. I understand that it should be done via interfaces, but I wanted to really understand how I would do it with classes for sake of learning Ninject. I have expanded my answer to include proper implementation with Interfaces. Also I have awarded you the bounty for helping me understand all of this. So thanks again! – Bagzli Apr 01 '16 at 21:11
  • @Bagzli - thank you! Not sure if you tried but my answer has 2 parts, the first part does do the injection of `UserManager` and `SignInManager` into the `AccountController` just like you asked. The second part extends it to also allow for injection of the new interface and implementation. – Igor Apr 02 '16 at 10:06
  • 1
    @Bagzli - about your addition. The idea is to hide the UserManager and SignInManager from your controllers and only access functionality through your new `IUserManagerSegment`. This would make it easier to test as well as change an implementation later on. Also if you want something Disposable then implement `System.IDisposable` so your signature would be `public interface IUserManagerSegment:IDisposable` and then remove your Dispose methods from your interface. This way the CLR recognizes it as such and you can use a using block if you wanted. – Igor Apr 02 '16 at 10:08
  • @Igor I understand that, thank you! Also thanks about the DIsposable tip, didn't know it can work like that. – Bagzli Apr 02 '16 at 16:14
  • Thank You! For me the missing step was as simple as moving the UserManager configuration out of the create method and into the controller then removing the lines from Startup.Auth. My MVC project is setup with only view concerns so the UserManager lives in a separate Application project, my User Model lives in a Models project and my UserStore is in a Data access project. Binders are in a Ninject Module project. – Ryan McArthur Oct 09 '17 at 17:33
  • @RyanMcArthur I've learned a lot since I've posted this question. one thing i would advise is if you are splitting your data layer, keep in mind that limitation of EF6 framework is that ALL your tables have to be defined in a single DB Context or you will run int nightmare scenarios where one db context updates a value in the database and the second has no idea it happened so it doesn't have that value or it tries to insert a duplicate record. There are some ways around this, but its so troublesome that you are better off using dapper instead. – Bagzli Oct 09 '17 at 18:11