7

I am migrating tens of thousands of users from an old website that didn't have a password in the database to this new web application, however, when I try to import the users using the async method, it ends up taking several days to the point where I just ended up cancelling it after a few days.

Now I have resorted to just creating new users directly from _context.Users.Add and assigning their roles, which i can do without a problem.. However, I can't seem to figure out how to create a generic password (all the same password) as these users will just be given a password to view a livestream (doesn't need to be super secure), but I still need the security part for the admin accounts that handle other stuff through the client/admin side UI. If a user signs in, I will have it automatically enter the default password for them.

For some reason though, I cannot get the password hasher to work correctly, as when I sign in, it says that the password is wrong...

This is what I'm using to generate the password and create the users...

 var appUser = new ApplicationUser() {
  Id = GenerateId(),
   AccessFailedCount = 0,
   Email = user[1],
   PasswordHash = "",
   FullName = "Standard User",
   UserName = user[1],
   PhoneNumber = user[8],
   FirstName = user[2],
   LastName = user[3],
   JoinMailingList = user[4],
   Country = user[5],
   City = user[6],
   StateRegion = user[7]
 };

 _context.Users.Add(appUser);

 var options = new PasswordHasherOptions();
 options.CompatibilityMode = PasswordHasherCompatibilityMode.IdentityV2;

 var hasher = new PasswordHasher < ApplicationUser > ();
 appUser.PasswordHash = hasher.HashPassword(appUser, "Default8!");

 var role = _context.Roles.FirstOrDefault(r => r.Name == "user");

 if (role != null) {
  var userRole = new IdentityUserRole < string > ();
  userRole.RoleId = role.Id;
  userRole.UserId = appUser.Id;
  _context.UserRoles.Add(userRole);
 }
}

_context.SaveChanges();

Can anyone help me out with how I'm supposed to Hash a password to store into the database?

Alex Liosatos
  • 321
  • 1
  • 4
  • 15

3 Answers3

13

If a user signs in, I will have it automatically enter the default password for them.

If you are using .net core Identity, you can use UserManager.CreateAsync to create the specified user in the backing store with given password:

public virtual System.Threading.Tasks.Task<Microsoft.AspNetCore.Identity.IdentityResult> CreateAsync (TUser user, string password);

Code below is for your reference:

var user = new ApplicationUser { UserName = "wx2@hotmail.com", Email = "wx2@hotmail.com" };
var result = await _userManager.CreateAsync(user, "YourPassWord");
if (result.Succeeded)
{

}

The Identity system will help create the password hash and store in the database . If you still need to manually hash the password , see IPasswordHasher interface .


Edit:

If you want to directly insert/update via database context, you should set correct NormalizedUserName and SecurityStamp to make the system work:

ApplicationUser applicationUser = new ApplicationUser();
Guid guid = Guid.NewGuid();
applicationUser.Id = guid.ToString();
applicationUser.UserName = "wx@hotmail.com";
applicationUser.Email = "wx@hotmail.com";
applicationUser.NormalizedUserName = "wx@hotmail.com";

_context.Users.Add(applicationUser);


var hasedPassword = _passwordHasher.HashPassword(applicationUser, "YourPassword");
applicationUser.SecurityStamp = Guid.NewGuid().ToString();
applicationUser.PasswordHash = hasedPassword;

_context.SaveChanges();
Uwe Keim
  • 39,551
  • 56
  • 175
  • 291
Nan Yu
  • 26,101
  • 9
  • 68
  • 148
  • I don't seem to have the ability to enter a password as a parameter for CreateAsync. It asks for ApplicationUser and CancellationToken. – Alex Liosatos Apr 24 '19 at 14:16
  • I have a CreateUserAsync from the AccountManager. The problem is that this method takes an extremely long time to complete. Especially for over 100k accounts. It seemingly creating one account half a second or second. – Alex Liosatos Apr 24 '19 at 15:05
  • 1
    @AlexLiosatos , see update reply and check whether it helps . – Nan Yu Apr 25 '19 at 02:20
  • @AlexLiosatos it took me between 3,5 and 4 minutes to create 1000 Application Users with "await userManager.CreateAsync(applicationUser, "Test123!");" (and "userManager.AddClaimsAsync"). I generated applicationUser in a for loop with SubjectId "TestSubjectId" + the index, Name "TestName" + the index and some Claimes. – SergSam Apr 25 '19 at 11:31
  • Using the DbContext it took ~26s for 1000 Users and finally using DbContext and addRange it took me around 12s but without Adding Claims. Use the DbContext as NanYu said, it helps alot! – SergSam Apr 25 '19 at 11:58
  • Marked this as the correct answer.. In all reality, it was the security stamp that was holding me back.. Once I added that in, it worked out and all 100k were created in just a couple minutes using _context. The issue I had is that logins still wouldn't work without a proper security stamp. – Alex Liosatos Apr 25 '19 at 14:23
  • Thousands of users could be created within a minutes if you will just create a Console App and add users directly in database (of course you will have to add proper Nuget packages in your App, such as Microsoft.AspNetCore.Identity etc.) – undead10 Sep 23 '21 at 21:22
2

As an addition, if you just want to Update the Password field of an given User:

var oldUser = await _userManager.GetUserAsync(User);

var result = await _userManager.ChangePasswordAsync(oldUser,
                    updateUserVm.CurrentPassword,
                    updateUserVm.NewPassword);

And an example to the Question "How I'm supposed to Hash a password?". You could Hash a registered Users Password with the UserManager-Referenced PasswordHasher like this:

ApplicationUser user = _userManager.Users...;
user.PasswordHash = _userManager.PasswordHasher.HashPassword(user, newPassword);
SergSam
  • 365
  • 1
  • 4
  • 16
0

I write my class PasswordHasher based on .net6 PasswordHasher docs latest version (V3) in this stackoverflow answer : https://stackoverflow.com/a/72429730/9875486

M Komaei
  • 7,006
  • 2
  • 28
  • 34