ASP.NET Identity is a bit more complicated, I would say.
In August 2014, they announced a new version of 2.1 , and everything changed again.
First of all, get rid of EntityFramework :
Uninstall-Package Microsoft.AspNet.Identity.EntityFramework
Now we implement our own User definition, which implements the IUser interface ( Microsoft.AspNet.Identity ):
public class User: IUser<int> { public User() { this.Roles = new List<string>(); this.Claims = new List<UserClaim>(); } public User(string userName) : this() { this.UserName = userName; } public User(int id, string userName): this() { this.Id = Id; this.UserName = userName; } public int Id { get; set; } public string UserName { get; set; } public string PasswordHash { get; set; } public bool LockoutEnabled { get; set; } public DateTime? LockoutEndDateUtc { get; set; } public bool TwoFactorEnabled { get; set; } public IList<string> Roles { get; private set; } public IList<UserClaim> Claims { get; private set; } }
As you can see, I have defined the type of my Id ( int ).
Then you must define your own UserManager inheritance from Microsoft.AspNet.Identity.UserManager with the user type and key type.
public class UserManager : UserManager<User, int> { public UserManager(IUserStore<User, int> store): base(store) { this.UserLockoutEnabledByDefault = false; // this.DefaultAccountLockoutTimeSpan = TimeSpan.FromMinutes(10); // this.MaxFailedAccessAttemptsBeforeLockout = 10; this.UserValidator = new UserValidator<User, int>(this) { AllowOnlyAlphanumericUserNames = false, RequireUniqueEmail = false }; // Configure validation logic for passwords this.PasswordValidator = new PasswordValidator { RequiredLength = 4, RequireNonLetterOrDigit = false, RequireDigit = false, RequireLowercase = false, RequireUppercase = false, }; } }
I have implemented my validation rules here, but you can leave them outside if you want.
UserManager needs UserStore ( IUserStore ).
Here you define the logic of the database. There are several interfaces to implement. Not all of them are mandatory though.
public class UserStore : IUserStore<User, int>, IUserPasswordStore<User, int>, IUserLockoutStore<User, int>, IUserTwoFactorStore<User, int>, IUserRoleStore<User, int>, IUserClaimStore<User, int> { // You can inject connection string or db session public UserStore() { } }
I have not included all methods for each interface. Once you do this, you can write a new user:
public System.Threading.Tasks.Task CreateAsync(User user) { }
select it using id:
public System.Threading.Tasks.Task<User> FindByIdAsync(int userId) { }
etc.
Then you need to define your SignInManager inheriting from Microsoft.AspNet.Identity.Owin.SignInManager .
public class SignInManager: SignInManager<User, int> { public SignInManager(UserManager userManager, IAuthenticationManager authenticationManager): base(userManager, authenticationManager) { } public override Task SignInAsync(User user, bool isPersistent, bool rememberBrowser) { return base.SignInAsync(user, isPersistent, rememberBrowser); } }
I just implemented SignInAsync : it will create ClaimsIdentity .
This is pretty much the case.
Now in the Startup class, you must tell Owin how to create a UserManager and SignInManager .
app.CreatePerOwinContext<Custom.Identity.UserManager>(() => new Custom.Identity.UserManager(new Custom.Identity.UserStore())); // app.CreatePerOwinContext<Custom.Identity.RoleManager>(() => new Custom.Identity.RoleManager(new Custom.Identity.RoleStore())); app.CreatePerOwinContext<Custom.Identity.SignInService>((options, context) => new Custom.Identity.SignInService(context.GetUserManager<Custom.Identity.UserManager>(), context.Authentication));
I did not use the factories that you will find in the default template, because I wanted everything to be as simple as possible.
And enable the cookie application:
app.UseCookieAuthentication(new CookieAuthenticationOptions { AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie, LoginPath = new PathString("/Account/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<Custom.Identity.UserManager, Custom.Identity.User, int>( validateInterval: TimeSpan.FromMinutes(30), regenerateIdentityCallback: (manager, user) => { var userIdentity = manager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie); return (userIdentity); }, getUserIdCallback: (id) => (Int32.Parse(id.GetUserId())) )} });
Now in the controller of your account or the controller responsible for logging in, you will need to get the UserManager and SignInManager :
public Custom.Identity.SignInManager SignInManager { get { return HttpContext.GetOwinContext().Get<Custom.Identity.SignInManager>(); } } public Custom.Identity.UserManager UserManager { get { return HttpContext.GetOwinContext().GetUserManager<Custom.Identity.UserManager>(); } }
You will use SignInManager to log in:
var result = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: false);
and UserManager for creating a user, adding roles and claims:
if (ModelState.IsValid) { var user = new Custom.Identity.User() { UserName = model.Email }; var result = await UserManager.CreateAsync(user, model.Password); if (result.Succeeded) { // await UserManager.AddToRoleAsync(user.Id, "Administrators"); // await UserManager.AddClaimAsync(user.Id, new System.Security.Claims.Claim(System.Security.Claims.ClaimTypes.Country, "England")); await SignInManager.SignInAsync(user, isPersistent:false, rememberBrowser:false); return RedirectToAction("Index", "Home"); } AddErrors(result); }
It seems complicated ... and it ... kind of.
If you want to know more about this, there will be a good explanation here and.
If you want to run some code and see how it works, I have compiled some code that works with Biggy (since I did not want to spend a lot of time defining tables and the like).
If you have the opportunity to download my code from the github repository, you will notice that I created an additional project (Custom.Identity), where I saved all my ASP.NET ID material.
The only nuget packages you'll need:
- Microsoft.AspNet.Identity.Core
- Microsoft.AspNet.Identity.Owin