Rehash passwords when needed
Fixes https://github.com/aspnet/Identity/issues/17
This commit is contained in:
parent
41806a6067
commit
69ac9abcc5
|
|
@ -463,7 +463,7 @@ namespace Microsoft.AspNet.Identity
|
|||
{
|
||||
throw new ArgumentNullException("password");
|
||||
}
|
||||
var result = await UpdatePasswordInternal(passwordStore, user, password, cancellationToken);
|
||||
var result = await UpdatePasswordHash(passwordStore, user, password, cancellationToken);
|
||||
if (!result.Succeeded)
|
||||
{
|
||||
return result;
|
||||
|
|
@ -565,7 +565,12 @@ namespace Microsoft.AspNet.Identity
|
|||
{
|
||||
return false;
|
||||
}
|
||||
return await VerifyPasswordAsync(passwordStore, user, password, cancellationToken);
|
||||
var result = await VerifyPasswordAsync(passwordStore, user, password, cancellationToken);
|
||||
if (result == PasswordVerificationResult.SuccessRehashNeeded) {
|
||||
await UpdatePasswordHash(passwordStore, user, password, cancellationToken, validatePassword: false);
|
||||
await UpdateAsync(user, cancellationToken);
|
||||
}
|
||||
return result != PasswordVerificationResult.Failed;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -607,7 +612,7 @@ namespace Microsoft.AspNet.Identity
|
|||
{
|
||||
return IdentityResult.Failed(ErrorDescriber.UserAlreadyHasPassword());
|
||||
}
|
||||
var result = await UpdatePasswordInternal(passwordStore, user, password, cancellationToken);
|
||||
var result = await UpdatePasswordHash(passwordStore, user, password, cancellationToken);
|
||||
if (!result.Succeeded)
|
||||
{
|
||||
return result;
|
||||
|
|
@ -632,9 +637,9 @@ namespace Microsoft.AspNet.Identity
|
|||
{
|
||||
throw new ArgumentNullException("user");
|
||||
}
|
||||
if (await VerifyPasswordAsync(passwordStore, user, currentPassword, cancellationToken))
|
||||
if (await VerifyPasswordAsync(passwordStore, user, currentPassword, cancellationToken) != PasswordVerificationResult.Failed)
|
||||
{
|
||||
var result = await UpdatePasswordInternal(passwordStore, user, newPassword, cancellationToken);
|
||||
var result = await UpdatePasswordHash(passwordStore, user, newPassword, cancellationToken);
|
||||
if (!result.Succeeded)
|
||||
{
|
||||
return result;
|
||||
|
|
@ -659,21 +664,24 @@ namespace Microsoft.AspNet.Identity
|
|||
{
|
||||
throw new ArgumentNullException("user");
|
||||
}
|
||||
await passwordStore.SetPasswordHashAsync(user, null, cancellationToken);
|
||||
await UpdateSecurityStampInternal(user, cancellationToken);
|
||||
await UpdatePasswordHash(passwordStore, user, null, cancellationToken, validatePassword: false);
|
||||
return await UpdateAsync(user, cancellationToken);
|
||||
}
|
||||
|
||||
internal async Task<IdentityResult> UpdatePasswordInternal(IUserPasswordStore<TUser> passwordStore,
|
||||
TUser user, string newPassword, CancellationToken cancellationToken)
|
||||
internal async Task<IdentityResult> UpdatePasswordHash(IUserPasswordStore<TUser> passwordStore,
|
||||
TUser user, string newPassword, CancellationToken cancellationToken, bool validatePassword = true)
|
||||
{
|
||||
var validate = await ValidatePasswordInternal(user, newPassword, cancellationToken);
|
||||
if (!validate.Succeeded)
|
||||
if (validatePassword)
|
||||
{
|
||||
return validate;
|
||||
var validate = await ValidatePasswordInternal(user, newPassword, cancellationToken);
|
||||
if (!validate.Succeeded)
|
||||
{
|
||||
return validate;
|
||||
}
|
||||
}
|
||||
var hash = newPassword != null ? PasswordHasher.HashPassword(user, newPassword) : null;
|
||||
await
|
||||
passwordStore.SetPasswordHashAsync(user, PasswordHasher.HashPassword(user, newPassword), cancellationToken);
|
||||
passwordStore.SetPasswordHashAsync(user, hash, cancellationToken);
|
||||
await UpdateSecurityStampInternal(user, cancellationToken);
|
||||
return IdentityResult.Success;
|
||||
}
|
||||
|
|
@ -686,11 +694,11 @@ namespace Microsoft.AspNet.Identity
|
|||
/// <param name="password"></param>
|
||||
/// <param name="cancellationToken"></param>
|
||||
/// <returns></returns>
|
||||
protected virtual async Task<bool> VerifyPasswordAsync(IUserPasswordStore<TUser> store, TUser user,
|
||||
protected virtual async Task<PasswordVerificationResult> VerifyPasswordAsync(IUserPasswordStore<TUser> store, TUser user,
|
||||
string password, CancellationToken cancellationToken = default(CancellationToken))
|
||||
{
|
||||
var hash = await store.GetPasswordHashAsync(user, cancellationToken);
|
||||
return PasswordHasher.VerifyHashedPassword(user, hash, password) != PasswordVerificationResult.Failed;
|
||||
return PasswordHasher.VerifyHashedPassword(user, hash, password);
|
||||
}
|
||||
|
||||
// IUserSecurityStampStore methods
|
||||
|
|
@ -776,7 +784,7 @@ namespace Microsoft.AspNet.Identity
|
|||
return IdentityResult.Failed(ErrorDescriber.InvalidToken());
|
||||
}
|
||||
var passwordStore = GetPasswordStore();
|
||||
var result = await UpdatePasswordInternal(passwordStore, user, newPassword, cancellationToken);
|
||||
var result = await UpdatePasswordHash(passwordStore, user, newPassword, cancellationToken);
|
||||
if (!result.Succeeded)
|
||||
{
|
||||
return result;
|
||||
|
|
|
|||
|
|
@ -415,6 +415,34 @@ namespace Microsoft.AspNet.Identity.Test
|
|||
store.VerifyAll();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CheckPasswordWillRehashPasswordWhenNeeded()
|
||||
{
|
||||
// Setup
|
||||
var store = new Mock<IUserPasswordStore<TestUser>>();
|
||||
var hasher = new Mock<IPasswordHasher<TestUser>>();
|
||||
var user = new TestUser { UserName = "Foo" };
|
||||
var pwd = "password";
|
||||
var hashed = "hashed";
|
||||
var rehashed = "rehashed";
|
||||
|
||||
store.Setup(s => s.GetPasswordHashAsync(user, CancellationToken.None))
|
||||
.ReturnsAsync(hashed)
|
||||
.Verifiable();
|
||||
hasher.Setup(s => s.VerifyHashedPassword(user, hashed, pwd)).Returns(PasswordVerificationResult.SuccessRehashNeeded).Verifiable();
|
||||
hasher.Setup(s => s.HashPassword(user, pwd)).Returns(rehashed).Verifiable();
|
||||
var userManager = MockHelpers.TestUserManager(store.Object);
|
||||
userManager.PasswordHasher = hasher.Object;
|
||||
|
||||
// Act
|
||||
var result = await userManager.CheckPasswordAsync(user, pwd);
|
||||
|
||||
// Assert
|
||||
Assert.True(result);
|
||||
store.VerifyAll();
|
||||
hasher.VerifyAll();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RemoveClaimsCallsStore()
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue