Search code examples
c#asp.netasp.net-mvcasp.net-identitysimple-injector

Setup Simple Injector with ASP.NET Identity 2.2.1


So I've been trying to setup ASP.NET Identity 2.2.1 and I keep hitting roadblocks. For being something that is supposed to be auto-generated by the framework it sure seems complicated to configure. I think I'm almost there but I'm still having issues setting up IoC with Simple Injector.

I've been reading through other StackOverflow questions and blogs about these issues. Most of them seem out of date because they deal with ASP.NET Identity 1.0. There's even been enough changes from 2.0 to 2.2.1 I've run into some issues with out of date solutions. Or the solutions deal with other IoC frameworks and I just can't translate them to Simple Injector. I've used other IoC Frameworks but I'm new to Simple Injector.

UPDATE - Ric .Net's first link in his comment was very helpful. I've reformatted my work to fit his suggestions and I think I'm almost there. But I'm still having one issue. I've completed replaced the previous code with the code below.

1) I updated ApplicationDbContext:

public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
    public ApplicationDbContext(string connectionstring)
        : base(connectionstring, throwIfV1Schema: false)
    {
    }
}

2) I removed the Create() method from ApplicationUserManager and moved it into a class that initializes all of my Simple Injector code:

public static class SimpleInjectorInitializer
{
    public static Container Initialize(IAppBuilder app)
    {
        var container = GetInitializeContainer(app);

        container.Verify();

        DependencyResolver.SetResolver(
            new SimpleInjectorDependencyResolver(container));

        return container;
    }

    public static Container GetInitializeContainer(
              IAppBuilder app)
    {
        var container = new Container();

        container.RegisterSingle<IAppBuilder>(app);

        container.RegisterPerWebRequest<
               ApplicationUserManager>();

        container.RegisterPerWebRequest<ApplicationDbContext>(() 
          => new ApplicationDbContext(
           "Your constring goes here"));

        container.RegisterPerWebRequest<IUserStore<
          ApplicationUser>>(() => 
            new UserStore<ApplicationUser>(
              container.GetInstance<ApplicationDbContext>()));

        container.RegisterInitializer<ApplicationUserManager>(
            manager => InitializeUserManager(manager, app));

        container.RegisterMvcControllers(
                Assembly.GetExecutingAssembly());

        return container;
    }

    private static void InitializeUserManager(
        ApplicationUserManager manager, IAppBuilder app)
    {
        manager.UserValidator = 
         new UserValidator<ApplicationUser>(manager)
        {
            AllowOnlyAlphanumericUserNames = false,
            RequireUniqueEmail = true
        };

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

        var dataProtectionProvider = 
             app.GetDataProtectionProvider();

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

3) I changed the Configuration method in Startup.cs:

public void Configuration(IAppBuilder app)
{
    var container = SimpleInjectorInitializer.Initialize(app);
    ConfigureAuth(app, container);
}

4) I updated ConfigureAuth in Configure.Auth.cs:

public void ConfigureAuth(IAppBuilder app, Container container)
{
    // Configure the db context and user manager to use a single instance per request
    //app.CreatePerOwinContext(ApplicationDbContext.Create);
    app.CreatePerOwinContext(container.GetInstance<ApplicationUserManager>);
    // Rest of code here ...

5) Then I removed the default constructor and UserManager property from AccountsController.

The problem I'm having now is that since ASP.NET Identity 2.0, they have added the parameter ISecureDataFormat<AuthenticationTicket> accessTokenFormat to the AccountsController constructor.

public AccountController(ApplicationUserManager userManager, 
    ISecureDataFormat<AuthenticationTicket> accessTokenFormat)

So now, how do I add DI for ISecureDataFormat<AuthenticationTicket> in Simple Injector?


Solution

  • So between the help Ric .Net (which I've posted above as part of the question) and Lucas Teles answer in another question I've finally got my answer. Lucas suggested adding these lines to my Simple Injector setup:

    container.Register<ISecureDataFormat<AuthenticationTicket>,
        SecureDataFormat<AuthenticationTicket>>(Lifestyle.Scoped);
    container.Register<ITextEncoder, Base64UrlTextEncoder>(Lifestyle.Scoped);
    container.Register<IDataSerializer<AuthenticationTicket>, TicketSerializer>(
        Lifestyle.Scoped);
    container.Register<IDataProtector>(
        () => new Microsoft.Owin.Security.DataProtection.DpapiDataProtectionProvider()
            .Create("ASP.NET Identity"),
        Lifestyle.Scoped);
    

    So now my container building method looks like this:

    public static Container GetInitializeContainer(IAppBuilder app)
    {
        var container = new Container();
    
        // IoC for ASP.NET Identity
        container.RegisterSingleton<IAppBuilder>(app);
        container.Register<ApplicationUserManager>(Lifestyle.Scoped);
        container.Register<ApplicationDbContext>(
            () => new ApplicationDbContext("Your constring goes here"),
            Lifestyle.Scoped);
        container.Register<IUserStore<ApplicationUser>>(
            () => new UserStore<ApplicationUser>(
                container.GetInstance<ApplicationDbContext>()),
            Lifestyle.Scoped);
        container.RegisterInitializer<ApplicationUserManager>(
            manager => InitializeUserManager(manager, app));
        // Setup for ISecureDataFormat
        container.Register<ISecureDataFormat<AuthenticationTicket>, 
            SecureDataFormat<AuthenticationTicket>>(Lifestyle.Scoped);
        container.Register<ITextEncoder, Base64UrlTextEncoder>(Lifestyle.Scoped);
        container.Register<IDataSerializer<AuthenticationTicket>, 
            TicketSerializer>(Lifestyle.Scoped);
        container.Register<IDataProtector>(
            () => new Microsoft.Owin.Security.DataProtection.DpapiDataProtectionProvider()
                .Create("ASP.NET Identity"),
            Lifestyle.Scoped);
    
        // Register all controllers
        container.RegisterMvcControllers(Assembly.GetExecutingAssembly());
    
        return container;
    }