Passwort zum Zurücksetzen der ASP.NET-Identität

95

Wie kann ich das Kennwort eines Benutzers im neuen ASP.NET-Identitätssystem abrufen? Oder wie kann ich zurücksetzen, ohne das aktuelle zu kennen (Benutzer hat Passwort vergessen)?

Daniel
quelle

Antworten:

102

In der aktuellen Version

Angenommen, Sie haben die Überprüfung der Anforderung zum Zurücksetzen des vergessenen Kennworts durchgeführt, verwenden Sie den folgenden Code als Beispielcode.

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

Das Framework wurde aktualisiert, um mit Token für die Bearbeitung von Anforderungen wie ForgetPassword zusammenzuarbeiten. Nach der Veröffentlichung wird eine einfache Code-Anleitung erwartet.

Aktualisieren:

Dieses Update dient nur dazu, klarere Schritte bereitzustellen.

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
quelle
Wissen Sie, wann Version 1.1 veröffentlicht wird?
Graycrow
Es ist immer noch in Alpha und 1.0 ist gerade veröffentlicht worden. Nehmen Sie also viele Monate an. myget.org/gallery/aspnetwebstacknightly
jd4u
11
Seltsamerweise funktionierte der Methodenaufruf store.SetPasswordHashAsync (cUser, hashedNewPassword) bei mir nicht. Stattdessen musste ich cUser.PasswordHash = hashedNewPassword manuell festlegen und dann UserManager.UpdateAsync (Benutzer) aufrufen.
Andy Mehalick
1
Code, der nicht funktioniert, ist nur möglich, wenn der Benutzerabrufkontext und der Speicherkontext unterschiedlich sind. Der Code war nur ein Beispielschritt, nicht genau. Wird die Antwort bald aktualisieren, um dieses Problem für andere zu vermeiden.
JD4U
1
Framework 1 bietet nicht. Framework 2-alpha verfügt jedoch nur über wenige Funktionen, die einen einfachen Prozess für die Bearbeitung von Anforderungen zum Zurücksetzen von Kennwörtern bieten. aspnetidentity.codeplex.com
jd4u
137

Oder wie kann ich zurücksetzen, ohne das aktuelle zu kennen (Benutzer hat Passwort vergessen)?

Wenn Sie ein Kennwort mit dem UserManager ändern möchten, aber nicht das aktuelle Kennwort des Benutzers angeben möchten, können Sie ein Token zum Zurücksetzen des Kennworts generieren und es stattdessen sofort verwenden.

string resetToken = await UserManager.GeneratePasswordResetTokenAsync(model.Id);
IdentityResult passwordChangeResult = await UserManager.ResetPasswordAsync(model.Id, resetToken, model.NewPassword);
Daniel Wright
quelle
8
Dies ist bei weitem der beste und sauberste Weg, um ein neues Passwort festzulegen. Das Problem mit der akzeptierten Antwort besteht darin, dass die Validierung der Kennwortkomplexität umgangen wird, indem direkt auf den Kennwort-Hasher zugegriffen wird.
Chris
6
Zu Ihrer Information, Sie erhalten möglicherweise die Fehlermeldung "Kein IUserTokenProvider ist registriert." wenn Sie die obige Logik verwenden. Siehe diese stackoverflow.com/questions/22629936/… .
Prasad Kanaparthi
1
Dies funktioniert vermutlich nur für Microsoft.AspNet.Identity in Version 2. Sie können die GeneratePasswordResetTokenAsync-Methode in Version 1 nicht finden.
Romanoza
Vielen Dank für Ihre Antwort. Es funktioniert wie ein Zauber für mich.
Thomas.Benz
4
Wenn Sie ein ungültiges Token erhalten , stellen Sie sicher, dass das SecurityStampfür Ihren Benutzer nicht null ist. Dies kann bei Benutzern auftreten, die aus anderen Datenbanken migriert wurden, oder bei Benutzern, die nicht mithilfe einer UserManager.CreateAsync()Methode erstellt wurden.
Alisson
70

Veraltet

Dies war die ursprüngliche Antwort. Es funktioniert, hat aber ein Problem. Was ist, wenn es AddPasswordfehlschlägt? Der Benutzer hat kein Passwort.

Die ursprüngliche Antwort: Wir können drei Codezeilen verwenden:

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

userManager.RemovePassword(userId);

userManager.AddPassword(userId, newPassword);

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

Jetzt empfohlen

Es ist wahrscheinlich besser, die Antwort zu verwenden, die EdwardBrey vorgeschlagen und DanielWright später mit einem Codebeispiel ausgearbeitet hat .

Shaun Luttin
quelle
1
Gott sei Dank dafür dachte ich, ich müsste einen neuen User Store erstellen, bis ich das sah!
Luke
Gibt es eine Möglichkeit, dies direkt in SQL zu tun? Ich würde meinem DBA gerne einen Sproc geben, den er bei Bedarf anstelle einer ausführbaren Datei aufrufen kann.
Mark Richman
@ MarkRichman Das ist eine neue Frage. Sie können jedoch auch das generierte T-SQL überprüfen, das auf SQL Server ausgeführt wird.
Shaun Luttin
3
Achten Sie darauf, dass der Benutzer kein Kennwort erhält, wenn AddPassword fehlschlägt (dh unzureichende Kennwortkomplexität).
Chris
1
Nun, der sauberste Ansatz ohne Umgehung von Geschäftsregeln (denn wenn Sie direkt auf den Passwort-Hasher zugreifen, gibt es keine Überprüfung der Passwortkomplexität) ist der von Daniel Wright vorgeschlagene.
Chris
29

UserManagerRufen Sie bei Ihrem ersten Aufruf GeneratePasswordResetTokenAsync auf . Wenn der Benutzer seine Identität überprüft hat (z. B. durch Empfangen des Tokens in einer E-Mail), übergeben Sie das Token an ResetPasswordAsync .

Edward Brey
quelle
1
Der Versuch herauszufinden, warum ResetPasswordAsync eine Benutzer-ID erfordert und eine vernünftige Möglichkeit, diese vom Benutzer abzurufen, wenn er mit einem Token angezeigt wird. GeneratePasswordReset verwendet ein Token mit mehr als 150 Zeichen. Dies scheint ausreichend zu sein, um eine Benutzer-ID kryptografisch zu verstauen, sodass ich das nicht selbst implementieren muss. :(
pettys
Ich gehe davon aus, dass nach der Benutzer-ID gefragt wird, damit das Reset-Token für diese Benutzer-ID in die Identitätsdatenbank eingegeben werden kann. Wenn dies nicht der Fall wäre, wie würde das Framework jemals wissen, ob das Token gültig ist? Sie sollten in der Lage sein, die Benutzer-ID mit User.Identity.GetUserId () oder ähnlichem abzurufen.
Ryan Buddicom
1
Das Erfordernis der Benutzer-ID ist eine dumme Wahl seitens der API. Das Token befindet sich bereits in der Datenbank, wenn ResetPassword (async) aufgerufen wird, und es sollte ausreichen, um es nur anhand der Eingabe zu validieren.
Filip
@Filip, der Vorteil ResetPasswordAsynceiner Benutzer-ID besteht darin, dass der Identitätsanbieter nur Benutzer-IDs indizieren muss, nicht auch Token. Dadurch kann es besser skaliert werden, wenn viele Benutzer vorhanden sind.
Edward Brey
1
@ Edward Brey gut, wie holst du die Benutzer-ID für den Rücksetzaufruf?
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);
}

Dieser Codeausschnitt stammt aus dem auf github verfügbaren AspNetIdentitySample-Projekt

Sclarson
quelle
2

Methode erstellen 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);
}
tmg
quelle
1

Im Falle eines Zurücksetzens des Passworts wird empfohlen, es durch Senden eines Token zum Zurücksetzen des Passworts an die E-Mail-Adresse eines registrierten Benutzers zurückzusetzen und den Benutzer aufzufordern, ein neues Passwort anzugeben. Wenn Sie eine einfach zu verwendende .NET-Bibliothek über das Identity Framework mit Standardkonfigurationseinstellungen erstellt haben. Details finden Sie unter Blog-Link und Quellcode bei github.

Rahul Garg
quelle
1

Ich denke, Microsoft-Handbuch für ASP.NET Identity ist ein guter Anfang.

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

Hinweis:

Wenn Sie AccountController nicht verwenden und Ihr Kennwort nicht zurücksetzen möchten, verwenden Sie Request.GetOwinContext().GetUserManager<ApplicationUserManager>();. Wenn Sie nicht denselben OwinContext haben, müssen Sie einen neuen erstellen, DataProtectorTokenProviderwie er OwinContextverwendet wird. Standardmäßig anschauen App_Start -> IdentityConfig.cs. Sollte ungefähr so ​​aussehen new DataProtectorTokenProvider<ApplicationUser>(dataProtectionProvider.Create("ASP.NET Identity"));.

Könnte so erstellt werden:

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

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

Das DpapiDataProtectionProviderund DataProtectorTokenProvidermuss mit demselben Namen erstellt werden, damit ein Zurücksetzen des Kennworts funktioniert. Die Verwendung von Owin zum Erstellen des Token zum Zurücksetzen des Kennworts und zum Erstellen eines neuen Tokens DpapiDataProtectionProvidermit einem anderen Namen funktioniert nicht.

Code, den ich für die ASP.NET-Identität verwende:

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
quelle
1

Ich habe ein wenig nachgeforscht und die Lösung, die für mich funktioniert, war eine Mischung aus einigen Lösungen, die in diesem Beitrag gegründet wurden.

Ich kompiliere diese Lösung im Grunde und poste, was für mich funktioniert. In meinem Fall möchte ich kein Token aus dem .net-Kern verwenden.

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

}
AFetter
quelle
0

Beste Methode zum Zurücksetzen des Kennworts in Asp.Net Core Identity für die Web-API.

Hinweis * : Fehler () und Ergebnis () werden für den internen Gebrauch erstellt. Sie können zurückkehren, was Sie wollen.

        [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
quelle