Customizing OWIN / Katana Usermanager factory user behavior

There are many examples in OWIN / Katana to find users in a database based on a combination of username and password and generate a basic complaints principle, for example ...

var userManager = context.OwinContext.GetUserManager<ApplicationUserManager>(); ApplicationUser user = await userManager.FindAsync(context.UserName, context.Password); // generate claims here... 

This is great if you are creating a new application and want the Entity Framework to do the dirty work. But I have an eight-year monolithic website that has just been updated to use claims-based authentication. Our database click is done manually through DAL / SQL, and then the ClaimsIdentity property is created.

Some people assume that OWIN is easier to use than our manual approach, but I would like information from those who use it.

Is it possible to change how the UserManager factory finds users based on their credentials? Or is there any other approach that I skipped? All the samples that I can find on the Internet seem to take a template approach that allows the Entity Framework to create a database and manage searches.

+5
source share
1 answer

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
+12
source

Source: https://habr.com/ru/post/1210586/


All Articles