ASP.NET Identity reset password
Asked Answered
H

11

125

How can I get the password of a user in the new ASP.NET Identity system? Or how can I reset without knowing the current one (user forgot password)?

Heteronomy answered 22/10, 2013 at 17:6 Comment(0)
P
117

In current release

Assuming you have handled the verification of the request to reset the forgotten password, use following code as a sample code steps.

ApplicationDbContext =new ApplicationDbContext()
String userId = "<YourLogicAssignsRequestedUserId>";
String newPassword = "<PasswordAsTypedByUser>";
ApplicationUser cUser = UserManager.FindById(userId);
String hashedNewPassword = UserManager.PasswordHasher.HashPassword(newPassword);
UserStore<ApplicationUser> store = new UserStore<ApplicationUser>();            
store.SetPasswordHashAsync(cUser, hashedNewPassword);

In AspNet Nightly Build

The framework is updated to work with Token for handling requests like ForgetPassword. Once in release, simple code guidance is expected.

Update:

This update is just to provide more clear steps.

ApplicationDbContext context = new ApplicationDbContext();
UserStore<ApplicationUser> store = new UserStore<ApplicationUser>(context);
UserManager<ApplicationUser> UserManager = new UserManager<ApplicationUser>(store);
String userId = User.Identity.GetUserId();//"<YourLogicAssignsRequestedUserId>";
String newPassword = "test@123"; //"<PasswordAsTypedByUser>";
String hashedNewPassword = UserManager.PasswordHasher.HashPassword(newPassword);                    
ApplicationUser cUser = await store.FindByIdAsync(userId);
await store.SetPasswordHashAsync(cUser, hashedNewPassword);
await store.UpdateAsync(cUser);
Pteryla answered 22/10, 2013 at 19:29 Comment(9)
do you know when version 1.1 will be released?Uncircumcision
Its still in alpha, and 1.0 is just released. So assume many months. myget.org/gallery/aspnetwebstacknightlyPteryla
Oddly the store.SetPasswordHashAsync(cUser, hashedNewPassword) method call did not work for me, instead I had to manually set cUser.PasswordHash = hashedNewPassword and then call UserManager.UpdateAsync(user);Chronicle
Code not working is only possible if the User retrieval context and store context are different. The code was only a sample steps, not accurate. Will soon update the answer to avoid this issue for others.Pteryla
Excellent. Your updated code is working perfectly for me. The only catch is it has to be inside an async method, which is fine for what I am doing. Thanks again!Xantho
"Assuming you have handled the verification of the request to reset the forgotten password". Does ASP.NET identity provide this functionality or do we have to write it from scratch. Do you have any references to examples of how to do this?Shilling
Framework 1 does not provide. But Framework 2-alpha does have few features that can provide simple process for handling password reset requests. aspnetidentity.codeplex.comPteryla
You got what I wanted. I put your code in an Action Result with [Authorize] tag, and at bottom I put "Response.Write("password changed for: " + cUser.NickName . . . . .then Return null. So, then since controller looks for query string first, I put parameters for userId and newPassword. Of course, I will have to be able to get the user's id from the database but I now have the ability to change a password for a friend who's computer is broken (on my website of course) :::::::::: public async Task<ActionResult> ChangePassword(string userId, string newOne) { //jd4u code hereMoe
yep, password not updated until calling store.UpdateAsyncBreedlove
P
181

Or how can I reset without knowing the current one (user forgot password)?

If you want to change a password using the UserManager but you do not want to supply the user's current password, you can generate a password reset token and then use it immediately instead.

string resetToken = await UserManager.GeneratePasswordResetTokenAsync(model.Id);
IdentityResult passwordChangeResult = await UserManager.ResetPasswordAsync(model.Id, resetToken, model.NewPassword);
Plasticine answered 24/3, 2015 at 15:23 Comment(6)
This is by far the best and cleanest way to set a new password. The problem with the accepted answer is that it bypasses the password complexity validations by directly accessing the password hasher.Sfumato
Fyi, You may get error 'No IUserTokenProvider is registered.' if you get use above logic. See this #22630436.Lots
This works for Microsoft.AspNet.Identity in version 2 only, I suppose. You can't find the GeneratePasswordResetTokenAsync method in version 1.Johnnie
I don't understand how you prompt the user for the new password. How can you verify the reset token without providing the new password as a parameter? Why is there not a separate method to validate the reset token so that you can prompt the user for a new password on a form so they can type their new password, and then reset it?Atrabilious
If you get Invalid Token, make sure the SecurityStamp for your user is not null. This may happen for users migrated from other databases, or users which were not created through UserManager.CreateAsync() method.Spoilt
@Alisson I'm using MongoDB, and i create user in different ways, ... how can i use token provider... reset password fails for me, and it seem, you are the only one pointing to my issueSama
P
117

In current release

Assuming you have handled the verification of the request to reset the forgotten password, use following code as a sample code steps.

ApplicationDbContext =new ApplicationDbContext()
String userId = "<YourLogicAssignsRequestedUserId>";
String newPassword = "<PasswordAsTypedByUser>";
ApplicationUser cUser = UserManager.FindById(userId);
String hashedNewPassword = UserManager.PasswordHasher.HashPassword(newPassword);
UserStore<ApplicationUser> store = new UserStore<ApplicationUser>();            
store.SetPasswordHashAsync(cUser, hashedNewPassword);

In AspNet Nightly Build

The framework is updated to work with Token for handling requests like ForgetPassword. Once in release, simple code guidance is expected.

Update:

This update is just to provide more clear steps.

ApplicationDbContext context = new ApplicationDbContext();
UserStore<ApplicationUser> store = new UserStore<ApplicationUser>(context);
UserManager<ApplicationUser> UserManager = new UserManager<ApplicationUser>(store);
String userId = User.Identity.GetUserId();//"<YourLogicAssignsRequestedUserId>";
String newPassword = "test@123"; //"<PasswordAsTypedByUser>";
String hashedNewPassword = UserManager.PasswordHasher.HashPassword(newPassword);                    
ApplicationUser cUser = await store.FindByIdAsync(userId);
await store.SetPasswordHashAsync(cUser, hashedNewPassword);
await store.UpdateAsync(cUser);
Pteryla answered 22/10, 2013 at 19:29 Comment(9)
do you know when version 1.1 will be released?Uncircumcision
Its still in alpha, and 1.0 is just released. So assume many months. myget.org/gallery/aspnetwebstacknightlyPteryla
Oddly the store.SetPasswordHashAsync(cUser, hashedNewPassword) method call did not work for me, instead I had to manually set cUser.PasswordHash = hashedNewPassword and then call UserManager.UpdateAsync(user);Chronicle
Code not working is only possible if the User retrieval context and store context are different. The code was only a sample steps, not accurate. Will soon update the answer to avoid this issue for others.Pteryla
Excellent. Your updated code is working perfectly for me. The only catch is it has to be inside an async method, which is fine for what I am doing. Thanks again!Xantho
"Assuming you have handled the verification of the request to reset the forgotten password". Does ASP.NET identity provide this functionality or do we have to write it from scratch. Do you have any references to examples of how to do this?Shilling
Framework 1 does not provide. But Framework 2-alpha does have few features that can provide simple process for handling password reset requests. aspnetidentity.codeplex.comPteryla
You got what I wanted. I put your code in an Action Result with [Authorize] tag, and at bottom I put "Response.Write("password changed for: " + cUser.NickName . . . . .then Return null. So, then since controller looks for query string first, I put parameters for userId and newPassword. Of course, I will have to be able to get the user's id from the database but I now have the ability to change a password for a friend who's computer is broken (on my website of course) :::::::::: public async Task<ActionResult> ChangePassword(string userId, string newOne) { //jd4u code hereMoe
yep, password not updated until calling store.UpdateAsyncBreedlove
I
71

Deprecated

This was the original answer. It does work, but has a problem. What if AddPassword fails? The user is left without a password.

The original answer: we can use three lines of code:

UserManager<IdentityUser> userManager = 
    new UserManager<IdentityUser>(new UserStore<IdentityUser>());

userManager.RemovePassword(userId);

userManager.AddPassword(userId, newPassword);

See also: http://msdn.microsoft.com/en-us/library/dn457095(v=vs.111).aspx

Now Recommended

It's probably better to use the answer that EdwardBrey proposed and then DanielWright later elaborated with a code sample.

Inrush answered 18/3, 2014 at 20:53 Comment(7)
Thank god for this, I thought I'd have to create a new user store until I saw this!Sulcate
Is there any way to do this directly in SQL? I'd love to hand my DBA a sproc to call when needed instead of an executable.Mannes
@MarkRichman That's a new question. One thing you could do, though, is to inspect the generated T-SQL that runs on SQL Server.Inrush
Watch out with this on, whenever AddPassword fails (ie insufficient password complexity), the user will be left without a password.Sfumato
@Sfumato This question and its answers try to address that: #29291866 Either create a custom ChangePassword method or use the existing AddPassword and keep trying until success.Inrush
Well the cleanest approach without bypassing any business rules (because when you access the password hasher directly there is no password complexity validation) is what Daniel Wright has proposed.Sfumato
@Sfumato Good call. I updated my answer to recommend Edward's answer (added 11 months prior to Daniel's.)Inrush
K
30

On your UserManager, first call GeneratePasswordResetTokenAsync. Once the user has verified his identity (for example by receiving the token in an email), pass the token to ResetPasswordAsync.

Kinchen answered 15/4, 2014 at 20:49 Comment(8)
Trying to figure out why ResetPasswordAsync requires a user ID, and a reasonable way to get it from the user when they show up with a token. GeneratePasswordReset uses a token that's over 150 chars... seems like that'd be enough to cryptographically stow a user id so I don't have to implement that myself. :(Deshawndesi
I assume It's asking for the user ID so it can enter the reset token into the Identity Database against that user ID. If it didn't do this how would the framework ever know if the token was valid. You should be able to pull the users ID using User.Identity.GetUserId() or similar.Dehumanize
Requiring the user id is a silly choice on the API's part, the token is already in the database when ResetPassword(async) is called and it should be enough just to validate it against the input.Overactive
@Filip, the advantage of ResetPasswordAsync taking a user ID is that the identity provider only needs to index user IDs, not also tokens. This lets it scale better if there are many users.Kinchen
@Edward Brey so...more roundtrips = better scalability?Overactive
@Filip, Extra round trip? You get a token with GeneratePasswordResetTokenAsync, email it to the user, wait for the user to give it back via a web browser, and then call ResetPasswordAsync. ResetPasswordAsync, does the equivalent of UPDATE Users SET Password = @newPassword WHERE UserId = @userId AND Token = @token. I don't see where an extra round trip comes in?Kinchen
@Edward Brey well, how do you fetch the user id for the reset call?Overactive
@Filip, as far as I know, they expect you to send the user ID along with the token in the email. It certainly does get verbose, adding more to an URL already quite long due to the size of the token.Kinchen
H
6

Best way to Reset Password in Asp.Net Core Identity use for Web API.

Note* : Error() and Result() are created for internal use. You can return you want.

        [HttpPost]
        [Route("reset-password")]
        public async Task<IActionResult> ResetPassword(ResetPasswordModel model)
        {
            if (!ModelState.IsValid)
                return BadRequest(ModelState);
            try
            {
                if (model is null)
                    return Error("No data found!");


                var user = await _userManager.FindByIdAsync(AppCommon.ToString(GetUserId()));
                if (user == null)
                    return Error("No user found!");

                Microsoft.AspNetCore.Identity.SignInResult checkOldPassword =
                    await _signInManager.PasswordSignInAsync(user.UserName, model.OldPassword, false, false);

                if (!checkOldPassword.Succeeded)
                    return Error("Old password does not matched.");

                string resetToken = await _userManager.GeneratePasswordResetTokenAsync(user);
                if (string.IsNullOrEmpty(resetToken))
                    return Error("Error while generating reset token.");

                var result = await _userManager.ResetPasswordAsync(user, resetToken, model.Password);

                if (result.Succeeded)
                    return Result();
                else
                    return Error();
            }
            catch (Exception ex)
            {
                return Error(ex);
            }
        }
Heroism answered 2/8, 2020 at 12:18 Comment(1)
This worked for me with Fx v 4.5 as well. The other solution did not work. Fundamentally this was much simpler too. You don't even really need to get the user since all the methods will accept the id. I just needed it for a temporary one-off reset in my admin interface so I didn't need all the error checks.Fideicommissary
V
3
string message = null;
//reset the password
var result = await IdentityManager.Passwords.ResetPasswordAsync(model.Token, model.Password);
if (result.Success)
{
    message = "The password has been reset.";
    return RedirectToAction("PasswordResetCompleted", new { message = message });
}
else
{
    AddErrors(result);
}

This snippet of code is taken out of the AspNetIdentitySample project available on github

Vesuvianite answered 22/10, 2013 at 17:48 Comment(0)
A
3

I think Microsoft guide for ASP.NET Identity is a good start.

https://learn.microsoft.com/en-us/aspnet/identity/overview/features-api/account-confirmation-and-password-recovery-with-aspnet-identity

Note:

If you do not use AccountController and wan't to reset your password, use Request.GetOwinContext().GetUserManager<ApplicationUserManager>();. If you dont have the same OwinContext you need to create a new DataProtectorTokenProvider like the one OwinContext uses. By default look at App_Start -> IdentityConfig.cs. Should look something like new DataProtectorTokenProvider<ApplicationUser>(dataProtectionProvider.Create("ASP.NET Identity"));.

Could be created like this:

Without Owin:

[HttpGet]
[AllowAnonymous]
[Route("testReset")]
public IHttpActionResult TestReset()
{
    var db = new ApplicationDbContext();
    var manager = new ApplicationUserManager(new UserStore<ApplicationUser>(db));
    var provider = new DpapiDataProtectionProvider("SampleAppName");
    manager.UserTokenProvider = new DataProtectorTokenProvider<ApplicationUser>(
        provider.Create("SampleTokenName"));

    var email = "[email protected]";

    var user = new ApplicationUser() { UserName = email, Email = email };

    var identityUser = manager.FindByEmail(email);

    if (identityUser == null)
    {
        manager.Create(user);
        identityUser = manager.FindByEmail(email);
    }

    var token = manager.GeneratePasswordResetToken(identityUser.Id);
    return Ok(HttpUtility.UrlEncode(token));
}

[HttpGet]
[AllowAnonymous]
[Route("testReset")]
public IHttpActionResult TestReset(string token)
{
    var db = new ApplicationDbContext();
    var manager = new ApplicationUserManager(new UserStore<ApplicationUser>(db));
    var provider = new DpapiDataProtectionProvider("SampleAppName");
    manager.UserTokenProvider = new DataProtectorTokenProvider<ApplicationUser>(
        provider.Create("SampleTokenName"));
    var email = "[email protected]";
    var identityUser = manager.FindByEmail(email);
    var valid = Task.Run(() => manager.UserTokenProvider.ValidateAsync("ResetPassword", token, manager, identityUser)).Result;
    var result = manager.ResetPassword(identityUser.Id, token, "TestingTest1!");
    return Ok(result);
}

With Owin:

[HttpGet]
[AllowAnonymous]
[Route("testResetWithOwin")]
public IHttpActionResult TestResetWithOwin()
{
    var manager = Request.GetOwinContext().GetUserManager<ApplicationUserManager>();

    var email = "[email protected]";

    var user = new ApplicationUser() { UserName = email, Email = email };

    var identityUser = manager.FindByEmail(email);

    if (identityUser == null)
    {
        manager.Create(user);
        identityUser = manager.FindByEmail(email);
    }

    var token = manager.GeneratePasswordResetToken(identityUser.Id);
    return Ok(HttpUtility.UrlEncode(token));
}

[HttpGet]
[AllowAnonymous]
[Route("testResetWithOwin")]
public IHttpActionResult TestResetWithOwin(string token)
{
    var manager = Request.GetOwinContext().GetUserManager<ApplicationUserManager>();

    var email = "[email protected]";
    var identityUser = manager.FindByEmail(email);
    var valid = Task.Run(() => manager.UserTokenProvider.ValidateAsync("ResetPassword", token, manager, identityUser)).Result;
    var result = manager.ResetPassword(identityUser.Id, token, "TestingTest1!");
    return Ok(result);
}

The DpapiDataProtectionProvider and DataProtectorTokenProvider needs to be created with the same name for a password reset to work. Using Owin for creating the password reset token and then creating a new DpapiDataProtectionProvider with another name won't work.

Code that I use for ASP.NET Identity:

Web.Config:

<add key="AllowedHosts" value="example.com,example2.com" />

AccountController.cs:

[Route("RequestResetPasswordToken/{email}/")]
[HttpGet]
[AllowAnonymous]
public async Task<IHttpActionResult> GetResetPasswordToken([FromUri]string email)
{
    if (!ModelState.IsValid)
        return BadRequest(ModelState);

    var user = await UserManager.FindByEmailAsync(email);
    if (user == null)
    {
        Logger.Warn("Password reset token requested for non existing email");
        // Don't reveal that the user does not exist
        return NoContent();
    }

    //Prevent Host Header Attack -> Password Reset Poisoning. 
    //If the IIS has a binding to accept connections on 80/443 the host parameter can be changed.
    //See https://security.stackexchange.com/a/170759/67046
    if (!ConfigurationManager.AppSettings["AllowedHosts"].Split(',').Contains(Request.RequestUri.Host)) {
            Logger.Warn($"Non allowed host detected for password reset {Request.RequestUri.Scheme}://{Request.Headers.Host}");
            return BadRequest();
    }

    Logger.Info("Creating password reset token for user id {0}", user.Id);

    var host = $"{Request.RequestUri.Scheme}://{Request.Headers.Host}";
    var token = await UserManager.GeneratePasswordResetTokenAsync(user.Id);
    var callbackUrl = $"{host}/resetPassword/{HttpContext.Current.Server.UrlEncode(user.Email)}/{HttpContext.Current.Server.UrlEncode(token)}";

    var subject = "Client - Password reset.";
    var body = "<html><body>" +
               "<h2>Password reset</h2>" +
               $"<p>Hi {user.FullName}, <a href=\"{callbackUrl}\"> please click this link to reset your password </a></p>" +
               "</body></html>";

    var message = new IdentityMessage
    {
        Body = body,
        Destination = user.Email,
        Subject = subject
    };

    await UserManager.EmailService.SendAsync(message);

    return NoContent();
}

[HttpPost]
[Route("ResetPassword/")]
[AllowAnonymous]
public async Task<IHttpActionResult> ResetPasswordAsync(ResetPasswordRequestModel model)
{
    if (!ModelState.IsValid)
        return NoContent();

    var user = await UserManager.FindByEmailAsync(model.Email);
    if (user == null)
    {
        Logger.Warn("Reset password request for non existing email");
        return NoContent();
    }            

    if (!await UserManager.UserTokenProvider.ValidateAsync("ResetPassword", model.Token, UserManager, user))
    {
        Logger.Warn("Reset password requested with wrong token");
        return NoContent();
    }

    var result = await UserManager.ResetPasswordAsync(user.Id, model.Token, model.NewPassword);

    if (result.Succeeded)
    {
        Logger.Info("Creating password reset token for user id {0}", user.Id);

        const string subject = "Client - Password reset success.";
        var body = "<html><body>" +
                   "<h1>Your password for Client was reset</h1>" +
                   $"<p>Hi {user.FullName}!</p>" +
                   "<p>Your password for Client was reset. Please inform us if you did not request this change.</p>" +
                   "</body></html>";

        var message = new IdentityMessage
        {
            Body = body,
            Destination = user.Email,
            Subject = subject
        };

        await UserManager.EmailService.SendAsync(message);
    }

    return NoContent();
}

public class ResetPasswordRequestModel
{
    [Required]
    [Display(Name = "Token")]
    public string Token { get; set; }

    [Required]
    [Display(Name = "Email")]
    public string Email { get; set; }

    [Required]
    [StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 10)]
    [DataType(DataType.Password)]
    [Display(Name = "New password")]
    public string NewPassword { get; set; }

    [DataType(DataType.Password)]
    [Display(Name = "Confirm new password")]
    [Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")]
    public string ConfirmPassword { get; set; }
}
Archiplasm answered 20/9, 2018 at 7:18 Comment(0)
T
2

Create method in UserManager<TUser, TKey>

public Task<IdentityResult> ChangePassword(int userId, string newPassword)
{
     var user = Users.FirstOrDefault(u => u.Id == userId);
     if (user == null)
          return new Task<IdentityResult>(() => IdentityResult.Failed());

     var store = Store as IUserPasswordStore<User, int>;
     return base.UpdatePassword(store, user, newPassword);
}
Tag answered 2/12, 2015 at 15:18 Comment(0)
A
2

In case of password reset, it is recommended to reset it through sending password reset token to registered user email and ask user to provide new password. If have created a easily usable .NET library over Identity framework with default configuration settins. You can find details at blog link and source code at github.

Aframe answered 5/8, 2016 at 19:11 Comment(0)
V
2

I did a little investigation and the solution that works for me was a mix of a few solutions founded in this post.

I'm basically compiling this solution and I'm posting what works for me. In my case, I'm don't want to use any token from .net core.

public async Task ResetPassword(string userId, string password)
{
    var user = await _userManager.FindByIdAsync(userId);
    var hashPassword= _userManager.PasswordHasher.HashPassword(user, password);
    user.PasswordHash = passwordHash;
    await _userManager.UpdateAsync(user);

}
Venita answered 10/4, 2020 at 11:42 Comment(1)
"what works for me" is just not good enough for something security relevant. I want to use as much premade .NET Core as possible.Absentminded
J
0

I will get straight to the point.

In order to update the password for an existing identity, first check if the identity exists. If it does, then generate a new Token for it in order to avoid the: No IUserTokenProvider is registered error if you will directly use the GeneratePasswordResetTokenAsync method. You can refer to below snippet on how to achieve both points in one go and then update the password:

using Microsoft.AspNet.Identity.Owin;
using Microsoft.Owin.Security.DataProtection;
using Microsoft.AspNet.Identity.EntityFramework;

public async void UpdatePasswordFromIdentity(string UserEmail)
{
    try
    {
        //Get user details based on the email first
        var resultuser = await _userManager.FindByEmailAsync(UserEmail);
        if (resultuser != null)
        {
            if (!string.IsNullOrEmpty(resultuser.UserName))
            {
                string generatedPassword = Membership.GeneratePassword(9, 3);
                var provider = new DpapiDataProtectionProvider("MyAppName");
                var userManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext()));
                userManager.UserTokenProvider = new DataProtectorTokenProvider<ApplicationUser>(provider.Create("ASP.NET Identity"));
                string resetToken = await userManager.GeneratePasswordResetTokenAsync(resultuser.Id);
                IdentityResult passwordChangeResult = await userManager.ResetPasswordAsync(resultuser.Id, resetToken, generatedPassword);
                if (passwordChangeResult.Succeeded)
                {
                    //Send email etc
                }
            }
        }
    }
    catch (Exception ex)
    {
        //Log exception
    }
}
Juvenescence answered 4/3 at 21:27 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.