Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Injecting SignInManager dependency: does not work with Unity, works when using OWIN

I am adding ASP.NET Identity authentication functionality to ASP.NET MVC 5 web application.

I am using Unity for dependency injection across the project, so I decided to inject dependencies required by the AccountController in the constructor:

public AccountController(UserManager<ApplicationUser> userManager, SignInManager<ApplicationUser, string> signInManager)
{
    _userManager = userManager;
    _signInManager = signInManager;
}

My Login method is implemented as following (actually, I copied that code from an ASP.NET Web Application project template with Individual User Accounts authentication):

//
// POST: /Account/Login
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
    var result = await _signInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: true);
    // Process result and return appropriate view...
    // However, there are no authentication cookies in the response!
}

The problem is that authentication does not work correctly - even if I entered correct credentials and result is SignInStatus.Success, there are no authentication cookies being sent in the response.

However, if I use OWIN infrastructure to resolve ApplicationSignInManager instead of Unity container, everything works correctly:

//
// POST: /Account/Login
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
    var owinSignInManager = HttpContext.GetOwinContext().Get<ApplicationSignInManager>();
    var result = await owinSignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: true);
    // Process result and return appropriate view...
    // Authentication cookies are present in the response!
}

That's how ApplicationSignInManager is registered in application Startup class:

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

And this is ApplicationSignInManager declaration:

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

    public static ApplicationSignInManager Create(IdentityFactoryOptions<ApplicationSignInManager> options, IOwinContext context)
    {
        var userManager = new ApplicationUserManager(new UserStore<ApplicationUser>(new DatabaseContext()));
        return new ApplicationSignInManager(userManager, context.Authentication);
    }
}

Here's a part of my Unity configuration:

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

The idea is that Unity provides the same dependencies to the ApplicationSignInManager constructor as Create method does. But Unity's approach does not work for some reason: no authentication cookies being sent after successful log in.

This is very specific question, but maybe someone faced a problem like this before? I believe this behavior should be related to OWIN middleware, pipeline and how all that stuff is being wired up at the application startup.

like image 761
Sergey Kolodiy Avatar asked Nov 15 '14 15:11

Sergey Kolodiy


2 Answers

Instead of registering IOwinContext in the container, do register IAuthenticationManager:

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

And have only one constructor for SignInManager:

public ApplicationSignInManager(ApplicationUserManager userManager, IAuthenticationManager authenticationManager)

I have done the registration with Unity like this, and here is the explanation.

like image 102
trailmax Avatar answered Sep 30 '22 18:09

trailmax


Just in case you need to inject the RoleManager as well, here's how it's done with Unity:

container.RegisterType<IRoleStore<IdentityRole, string>, RoleStore<IdentityRole>>(new InjectionConstructor(typeof(MyDbContext)));

Don't forget to register IAuthenticationManager as well (see above answer).

like image 22
Thorsten Westheider Avatar answered Sep 30 '22 20:09

Thorsten Westheider