4

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?

Community
  • 1
  • 1
Pinski
  • 2,607
  • 2
  • 24
  • 25
  • 2
    Did you read [this](https://simpleinjector.codeplex.com/discussions/564822) already? It doesn't discuss the 'IdentityFactoryOptions' but must give some guidance. Also see [this](http://stackoverflow.com/a/28948205/3294832) for a better method than the factory you mention. – Ric .Net May 07 '16 at 07:43
  • 1
    Have you read [this](https://simpleinjector.readthedocs.io/en/latest/LifestyleMismatches.html)? You should register the IdentityFactoryOptions as scoped or singleton. And no, it's not really you who is violating best peactices; it's Identity Feamework with its amazingly bad Visual Studio template. – Steven May 07 '16 at 08:14
  • 1
    [Here is a walkthrough](http://tech.trailmax.info/2014/09/aspnet-identity-and-ioc-container-registration/) that covers how to refactor the default MVC 5 template for integration with Autofac, SimpleInjector, or Unity. – NightOwl888 May 07 '16 at 12:03
  • Thanks everyone for the help and comments. @Ric.Net - Thanks for your first link. It was very helpful. This way seems much clearer than the way I previously cobbled together. I'm still running into a small issue though. – Pinski May 12 '16 at 03:46
  • @Steven - I had seen that, but it didn't help. I'm glad I'm not the only one that questions Identity Framework's VS template. – Pinski May 12 '16 at 03:47
  • @NightOwl888 - I tried using that version before, but it was very out of date. I believe it uses Identity 1.0. – Pinski May 12 '16 at 03:47

1 Answers1

5

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;
}
Steven
  • 166,672
  • 24
  • 332
  • 435
Pinski
  • 2,607
  • 2
  • 24
  • 25
  • This article also has some good notes about the `DataProtectionProvider` that normally comes from `IdentityFactoryOptions` - http://tech.trailmax.info/2014/09/aspnet-identity-and-ioc-container-registration/ – Wayne Bloss May 19 '16 at 22:55