Reputation: 159
I am working with ASP.NET MVC 5, EF6, ASP.NET Identity and Unity as IoC, and I want to use the functionalities provided by the Identity framework for manipulation with users and registration as I am building a website.
Startup.cs
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
}
public void ConfigureAuth(IAppBuilder app)
{
// Configure the db context, user manager and signin manager to use a single instance per request
app.CreatePerOwinContext(DbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);
app.CreatePerOwinContext<ApplicationRoleManager>(ApplicationRoleManager.Create);
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Auth/Login"),
Provider = new CookieAuthenticationProvider
{
// Enables the application to validate the security stamp when the user logs in.
// This is a security feature which is used when you change a password or add an external login to your account.
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
}
});
}
}
IdentityConfig.cs
public class EmailService : IIdentityMessageService
{
public async Task SendAsync(IdentityMessage message)
{
await configSendGridasync(message);
}
private async Task configSendGridasync(IdentityMessage message)
{
var myMessage = new SendGridMessage();
myMessage.AddTo(message.Destination);
myMessage.From = new System.Net.Mail.MailAddress("[email protected]", "JOhn Smith");
myMessage.Subject = message.Subject;
myMessage.Text = message.Body;
myMessage.Html = message.Body;
var creds = new NetworkCredential("user", "pass");
var transport = new Web(creds);
if (transport != null)
{
await transport.DeliverAsync(myMessage);
}
else
{
Trace.TraceError("Failed to create Web transport.");
await Task.FromResult(0);
}
}
}
// Configure the application user manager used in this application. UserManager is defined in ASP.NET Identity and is used by the application.
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<DbContext>()));
// 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 = false,
RequireDigit = true,
RequireLowercase = true,
RequireUppercase = false,
};
// Configure user lockout defaults
manager.UserLockoutEnabledByDefault = true;
manager.DefaultAccountLockoutTimeSpan = TimeSpan.FromMinutes(5);
manager.MaxFailedAccessAttemptsBeforeLockout = 5;
manager.EmailService = new EmailService();
var dataProtectionProvider = options.DataProtectionProvider;
if (dataProtectionProvider != null)
{
manager.UserTokenProvider =
new DataProtectorTokenProvider<ApplicationUser>(dataProtectionProvider.Create("ASP.NET Identity"));
}
return manager;
}
}
// Configure the application sign-in manager which is used in this application.
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);
}
}
public class ApplicationRoleManager : RoleManager<IdentityRole>
{
public ApplicationRoleManager(IRoleStore<IdentityRole, string> roleStore)
: base(roleStore)
{
}
public static ApplicationRoleManager Create(
IdentityFactoryOptions<ApplicationRoleManager> options,
IOwinContext context)
{
var manager = new ApplicationRoleManager(
new RoleStore<IdentityRole>(
context.Get<DbContext>()));
return manager;
}
}
UnityConfig.cs
public class UnityConfig
{
#region Unity Container
private static Lazy<IUnityContainer> container = new Lazy<IUnityContainer>(() =>
{
var container = new UnityContainer();
RegisterTypes(container);
return container;
});
/// <summary>
/// Gets the configured Unity container.
/// </summary>
public static IUnityContainer GetConfiguredContainer()
{
return container.Value;
}
#endregion
public static void RegisterTypes(IUnityContainer container)
{
#region AutoMapper DI
var config = new MapperConfiguration(cfg =>
{
cfg.AddProfile<AutoMapperWebProfileConfiguration>();
});
var mapper = config.CreateMapper();
container.RegisterInstance(config.CreateMapper());
#endregion
#region userManager and roleManager DI
var accountInjectionConstructor = new InjectionConstructor(new DbContext());
container.RegisterType<IUserStore<ApplicationUser>, UserStore<ApplicationUser>>(accountInjectionConstructor);
container.RegisterType<IRoleStore<IdentityRole, string>,
RoleStore<IdentityRole, string, IdentityUserRole>>(accountInjectionConstructor);
#endregion
#region AuthenticationManager
container.RegisterType<IAuthenticationManager>(new InjectionFactory(o => HttpContext.Current.GetOwinContext().Authentication));
#endregion
#region IdentityConfig injects
container.RegisterType<DbContext>();
container.RegisterType<ApplicationSignInManager>();
container.RegisterType<ApplicationUserManager>();
container.RegisterType<EmailService>();
container.RegisterType<IMappingEngine>(new InjectionFactory(_ => Mapper.Engine));
#endregion
}
}
AuthController.cs
public class AuthController : Controller
{
private IMapper mapper;
private RoleManager<IdentityRole> roleManager;
private IAuthenticationManager authManager;
private ApplicationUserManager userManager;
private ApplicationSignInManager signInManager;
public AuthController(IMapper mapper, IRoleStore<IdentityRole, string> roleStore, IAuthenticationManager authManager, ApplicationUserManager userManager, ApplicationSignInManager signInManager)
{
this.mapper = mapper;
this.roleManager = new RoleManager<IdentityRole>(roleStore);
this.authManager = authManager;
this.userManager = userManager;
this.signInManager = signInManager;
} //rest omitted for brevity.
The problem
When I run the code, everything in Startup.cs gets executed and all objects instantiated from the IdentityConfig.cs, so I get an instance of UserManager that has all the needed properties set (email service, userTokenprovider etc), but when my controller is reached, another object gets injected which does not have these properties set, hence I get many exceptions (e.g. IUserTokenProvider not registered etc). I assume that the UserManager that gets injected into the Controller is the one that Unity injects, and not the one that is created by Startup.cs. Same story with the rest private properties defined in the controller. How can I tell to Unity to use the objects created by the Startup.cs?
Upvotes: 4
Views: 2731
Reputation: 14741
Obviously OWIN and Unity are making own Identity objects in your code so depend on using, you would receive different object. To avoid this, you have to decide which one is responsible to create your object, OWIN or Unity. Since it is recommended to let DI do such thing it would be much better to change your start up file like this:
public void ConfigureAuth(IAppBuilder app)
{
app.CreatePerOwinContext(()=> DependencyResolver.Current.GetService<ApplicationUserManager>());
app.CreatePerOwinContext(()=> DependencyResolver.Current.GetService<ApplicationSignInManager>());
// other configs
}
Now OWIN gets same objects from Unity. But you also need some extra configuration and consideration to integrate Unity with Identity. I've already answered this have look.
Upvotes: 6