3

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
  • 171
  • 2
  • 14

3 Answers3

10

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();
Nan Yu
  • 23,741
  • 6
  • 55
  • 129
  • 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
1

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
  • 325
  • 4
  • 14
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
  • 4,146
  • 2
  • 14
  • 23