ASP.NET Identity reset password

95

Bagaimana cara mendapatkan kata sandi pengguna di sistem Identitas ASP.NET? Atau bagaimana cara mereset tanpa mengetahui yang sekarang (pengguna lupa kata sandi)?

daniel
sumber

Jawaban:

102

Dalam rilis saat ini

Dengan asumsi Anda telah menangani verifikasi permintaan untuk menyetel ulang sandi yang terlupa, gunakan kode berikut sebagai contoh kode langkah.

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);

Di AspNet Nightly Build

Kerangka kerja diperbarui untuk bekerja dengan Token untuk menangani permintaan seperti ForgetPassword. Setelah dirilis, diharapkan panduan kode sederhana.

Memperbarui:

Pembaruan ini hanya untuk memberikan langkah yang lebih jelas.

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);
jd4u
sumber
tahukah anda kapan versi 1.1 akan dirilis?
Graycrow
Ini masih dalam alfa, dan 1.0 baru saja dirilis. Jadi asumsikan berbulan-bulan. myget.org/gallery/aspnetwebstacknightly
jd4u
11
Anehnya, panggilan metode store.SetPasswordHashAsync (cUser, hashedNewPassword) tidak berfungsi untuk saya, sebagai gantinya saya harus menyetel cUser.PasswordHash = hashedNewPassword secara manual, lalu memanggil UserManager.UpdateAsync (user);
Andy Mehalick
1
Kode tidak berfungsi hanya mungkin jika konteks pengambilan pengguna dan konteks penyimpanan berbeda. Kode itu hanya contoh langkah, tidak akurat. Akan segera memperbarui jawaban untuk menghindari masalah ini untuk orang lain.
jd4u
1
Kerangka 1 tidak menyediakan. Tetapi Framework 2-alpha memang memiliki beberapa fitur yang dapat menyediakan proses sederhana untuk menangani permintaan pengaturan ulang kata sandi. aspnetidentity.codeplex.com
jd4u
138

Atau bagaimana cara mereset tanpa mengetahui yang sekarang (pengguna lupa kata sandi)?

Jika Anda ingin mengubah sandi menggunakan UserManager tetapi tidak ingin memberikan sandi pengguna saat ini, Anda dapat membuat token setel ulang sandi, lalu langsung menggunakannya.

string resetToken = await UserManager.GeneratePasswordResetTokenAsync(model.Id);
IdentityResult passwordChangeResult = await UserManager.ResetPasswordAsync(model.Id, resetToken, model.NewPassword);
Daniel Wright
sumber
8
Sejauh ini, ini adalah cara terbaik dan terbersih untuk mengatur kata sandi baru. Masalah dengan jawaban yang diterima adalah bahwa ia melewati validasi kompleksitas kata sandi dengan langsung mengakses penyimpan kata sandi.
Chris
6
Perlu diketahui, Anda mungkin mendapatkan kesalahan 'Tidak ada IUserTokenProvider terdaftar.' jika Anda menggunakan logika di atas. Lihat stackoverflow.com/questions/22629936/… ini .
Prasad Kanaparthi
1
Ini berfungsi untuk Microsoft.AspNet.Identity hanya dalam versi 2, saya kira. Anda tidak dapat menemukan metode GeneratePasswordResetTokenAsync di versi 1.
romanoza
Terima kasih atas jawaban Anda. Ini bekerja seperti pesona bagi saya.
Thomas.Benz
4
Jika Anda mendapatkan Token Tidak Valid , pastikan SecurityStampuntuk pengguna Anda tidak null. Ini mungkin terjadi untuk pengguna yang bermigrasi dari database lain, atau pengguna yang tidak dibuat melalui UserManager.CreateAsync()metode.
Alisson
70

Tidak digunakan lagi

Ini adalah jawaban aslinya. Itu berhasil, tapi punya masalah. Bagaimana jika AddPasswordgagal? Pengguna dibiarkan tanpa kata sandi.

Jawaban asli: kita bisa menggunakan tiga baris kode:

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

userManager.RemovePassword(userId);

userManager.AddPassword(userId, newPassword);

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

Sekarang Direkomendasikan

Mungkin lebih baik menggunakan jawaban yang diusulkan EdwardBrey dan kemudian DanielWright kemudian menguraikannya dengan sampel kode.

Shaun Luttin
sumber
1
Terima kasih Tuhan untuk ini, saya pikir saya harus membuat toko pengguna baru sampai saya melihat ini!
Lukas
Apakah ada cara untuk melakukan ini secara langsung di SQL? Saya ingin menyerahkan DBA saya sproc untuk dipanggil saat dibutuhkan, bukan yang dapat dieksekusi.
Mark Richman
@MarkRichman Itu pertanyaan baru. Namun, satu hal yang dapat Anda lakukan adalah memeriksa T-SQL yang dihasilkan yang berjalan di SQL Server.
Shaun Luttin
3
Hati-hati dengan ini, setiap kali AddPassword gagal (yaitu kerumitan kata sandi yang tidak mencukupi), pengguna akan dibiarkan tanpa kata sandi.
Chris
1
Pendekatan terbersih tanpa melewati aturan bisnis apa pun (karena ketika Anda mengakses penyimpan kata sandi secara langsung, tidak ada validasi kompleksitas kata sandi) adalah apa yang diusulkan Daniel Wright.
Chris
29

Di UserManager, panggil pertama GeneratePasswordResetTokenAsync . Setelah pengguna memverifikasi identitasnya (misalnya dengan menerima token di email), teruskan token tersebut ke ResetPasswordAsync .

Edward Brey
sumber
2
Mencoba mencari tahu mengapa ResetPasswordAsync memerlukan ID pengguna, dan cara yang wajar untuk mendapatkannya dari pengguna saat mereka muncul dengan token. GeneratePasswordReset menggunakan token yang lebih dari 150 karakter ... sepertinya itu sudah cukup untuk menyimpan ID pengguna secara kriptografis jadi saya tidak perlu mengimplementasikannya sendiri. :(
pettys
Saya berasumsi Ini meminta ID pengguna sehingga dapat memasukkan token reset ke Database Identitas terhadap ID pengguna itu. Jika tidak melakukan ini, bagaimana framework tahu apakah token itu valid. Anda harus dapat menarik ID pengguna menggunakan User.Identity.GetUserId () atau yang serupa.
Ryan Buddicom
1
Membutuhkan id pengguna adalah pilihan konyol di bagian API, token sudah ada di database ketika ResetPassword (async) dipanggil dan seharusnya cukup hanya untuk memvalidasinya terhadap input.
Filip
@Filip, keuntungan ResetPasswordAsyncmengambil ID pengguna adalah penyedia identitas hanya perlu mengindeks ID pengguna, tidak juga token. Ini memungkinkan skalanya lebih baik jika ada banyak pengguna.
Edward Brey
2
@Edward Brey baik, bagaimana Anda mendapatkan ID pengguna untuk panggilan reset?
Filip
2
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);
}

Cuplikan kode ini diambil dari proyek AspNetIdentitySample yang tersedia di github

sclarson.dll
sumber
2

Buat metode di 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);
}
tmg
sumber
2

Cara terbaik untuk Mereset Kata Sandi dalam penggunaan Identitas Inti Asp.Net untuk API Web.

Catatan * : Kesalahan () dan Hasil () dibuat untuk penggunaan internal. Anda dapat mengembalikan yang Anda inginkan.

        [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);
            }
        }
Manish Vadher
sumber
2
Ini bekerja untuk saya dengan Fx v 4.5 juga. Solusi lainnya tidak berhasil. Pada dasarnya ini juga jauh lebih sederhana. Anda bahkan tidak perlu mendapatkan pengguna karena semua metode akan menerima id. Saya hanya membutuhkannya untuk pengaturan ulang sementara di antarmuka admin saya jadi saya tidak memerlukan semua pemeriksaan kesalahan.
Steve Hiner
1

Dalam kasus pengaturan ulang kata sandi, disarankan untuk mengatur ulang melalui pengiriman token pengaturan ulang kata sandi ke email pengguna terdaftar dan meminta pengguna untuk memberikan kata sandi baru. Jika telah membuat pustaka .NET yang dapat digunakan dengan mudah melalui kerangka kerja Identitas dengan pengaturan konfigurasi default. Anda dapat menemukan detailnya di tautan blog dan kode sumber di github.

Rahul Garg
sumber
1

Saya pikir panduan Microsoft untuk Identitas ASP.NET adalah awal yang baik.

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

catatan:

Jika Anda tidak menggunakan AccountController dan tidak ingin mengatur ulang kata sandi, gunakan Request.GetOwinContext().GetUserManager<ApplicationUserManager>();. Jika Anda tidak memiliki OwinContext yang sama, Anda perlu membuat yang baru DataProtectorTokenProviderseperti yang OwinContextdigunakan. Secara default, lihat App_Start -> IdentityConfig.cs. Akan terlihat seperti new DataProtectorTokenProvider<ApplicationUser>(dataProtectionProvider.Create("ASP.NET Identity"));.

Bisa dibuat seperti ini:

Tanpa 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);
}

Dengan 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 DpapiDataProtectionProviderdan DataProtectorTokenProviderkebutuhan yang akan dibuat dengan nama yang sama untuk reset password untuk bekerja. Menggunakan Owin untuk membuat token reset kata sandi dan kemudian membuat yang baru DpapiDataProtectionProviderdengan nama lain tidak akan berfungsi.

Kode yang saya gunakan untuk 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; }
}
Ogglas
sumber
1

Saya melakukan sedikit investigasi dan solusi yang berhasil untuk saya adalah campuran dari beberapa solusi yang ditemukan dalam posting ini.

Saya pada dasarnya menyusun solusi ini dan saya memposting apa yang berhasil untuk saya. Dalam kasus saya, saya tidak ingin menggunakan token apa pun dari .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);

}
Lebih baik
sumber