1

We have customized Asp.Net Core to use our table of students rather than the AspNetUser table. Everything works fine for new students. But we need to update the existing students' passwords. I would like to do something like this (in AccountController Login method) when a student logins or this could be done on a one-time basis…

[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Login(LoginViewModel model, string returnUrl = null)
{
    ViewData["ReturnUrl"] = returnUrl;
    if (ModelState.IsValid)
    {
        // Require the user to have a confirmed email before they can log on.
        var user = await _userManager.FindByEmailAsync(model.Email);
        if (user != null)
        {
            if (user.PasswordHash == null)
            {
                user.EmailConfirmed = true;
                user.UserName = model.Email;
                user.NormalizedEmail = model.Email.ToUpper();
                user.NormalizedUserName = user.NormalizedEmail;
                //user.PasswordHash = ?;
                //user.SecurityStamp = ?;
                //update user in database.
            }
            //continue on with login process
        }
    }
}

The below code (from the Register method) creates a new user and adds him to the database. This is not what we want.

var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
var result = await _userManager.CreateAsync(user, model.Password);
if (result.Succeeded)
    // …
5
  • How is the user supposed to log in when the password hash is apparently null? What password would the user use then, and how would you check that it’s the actual user? Commented Dec 24, 2017 at 22:57
  • 1
    Why don’t you lock out all users that should change their password and then explain that they need to request a new password using the “password forgotten” link? That way, Identity will generate a password reset token which the users can then use to set their own password properly. Commented Dec 24, 2017 at 23:00
  • The students would be logging in with their plain text password. If I have an email and password match, I know it is an authorized user at least to the level it has been in the past. -- This code is before you get to the point where Identity system is checking the password hash. If I could make this work, the change would be completely transparent to the user. Commented Dec 24, 2017 at 23:27
  • 1
    So you would just reuse the existing password that is stored as plain text? (a) Why don’t you just convert all passwords in one go then, without the user having to login first? (b) That’s a terrible idea anyway. If you were storing passwords as plain text before, you should absolutely require users to change their password and store the new password only encrypted. Plain text passwords are broken, and users should know that they were exposed in plain text, so they avoid those forever. Commented Dec 24, 2017 at 23:46
  • Of course, this could be done in one go, However, one still needs to know how to do it so that is it produces the same hash and security stamp that the Identity system could use. Since the database has not been hacked and we use SSL, I don't think they have been exposed. The customer is trying to do the right thing. If you have an answer to this question, I would love to see it. Commented Dec 24, 2017 at 23:59

1 Answer 1

2

You’ll find the UserManager has anything you need to set the password for a user. While you could use the internal password hasher directly, going through the user manager makes sure that the user entity is updated properly in respect to the password. So you can just rely on the user manager to “do the right thing”.

If a user does not have a password, you can use AddPasswordAsync to set it:

var user = await _userManager.FindByEmailAsync(model.Email);
if (user != null && !(await _userManager.HasPasswordAsync(user)))
{
    // retrieve plaintext password
    var originalPassword = GetPlainTextPassword(user);

    var result = await _userManager.AddPasswordAsync(user, originalPassword);

    if (!result.Succeeded)
    {
        // handle error
    }
}

Otherwise, you could also make use of the password reset flow and generate a token you then immediately use to reset the user’s password (without actually involving the user). So you would basically chain GeneratePasswordResetTokenAsync and ResetPasswordAsync. Of course, this should only be done for maintenance reasons:

var user = await _userManager.FindByEmailAsync(model.Email);
if (user != null)
{
    // retrieve plaintext password
    var originalPassword = GetPlainTextPassword(user);

    // retrieve token
    var resetToken = await _userManager.GeneratePasswordResetTokenAsync(user);

    // reset password
    var result = await _userManager.ResetPasswordAsync(user, resetToken, originalPassword);

    if (!result.Succeeded)
    {
        // handle error
    }
}

Regardless of this, I would still suggest you to actively require users to reset their passwords themselves. Just remove the plain text passwords from the database, and keep the empty password. That way, users will have to reset their password first (you should add a note to explain that they will have to do that before their first login), and nobody will ever be able to see their new password in plain text. – The old passwords are exposed in the database and likely (hopefully?!) a large number of backups, and even if you believe your system is secure, it likely isn’t perfect. And there are still people involved that have access to the database and could directly or indirectly expose those passwords to others (with or without good intentions). You shouldn’t trust your system enough that nothing will go wrong. And users shouldn’t have to trust you with keeping their passwords safe. You should tell them to make a new password and get rid of their old ones—better sooner than later.

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks. I think this will work. Also, you convinced me on users explicitly resetting their password. Plus we can up the requirements for a stronger password.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.