mirror of
https://github.com/bitwarden/server.git
synced 2025-06-30 15:42:48 -05:00
feat(2FA): [PM-17129] Login with 2FA Recovery Code
* feat(2FA): [PM-17129] Login with 2FA Recovery Code - Login with Recovery Code working. * feat(2FA): [PM-17129] Login with 2FA Recovery Code - Feature flagged implementation. * style(2FA): [PM-17129] Login with 2FA Recovery Code - Code cleanup. * test(2FA): [PM-17129] Login with 2FA Recovery Code - Tests.
This commit is contained in:

committed by
GitHub

parent
465549b812
commit
ac6bc40d85
@ -730,6 +730,46 @@ public class UserServiceTests
|
||||
.RemoveAsync(Arg.Any<string>());
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task RecoverTwoFactorAsync_CorrectCode_ReturnsTrueAndProcessesPolicies(
|
||||
User user, SutProvider<UserService> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var recoveryCode = "1234";
|
||||
user.TwoFactorRecoveryCode = recoveryCode;
|
||||
|
||||
// Act
|
||||
var response = await sutProvider.Sut.RecoverTwoFactorAsync(user, recoveryCode);
|
||||
|
||||
// Assert
|
||||
Assert.True(response);
|
||||
Assert.Null(user.TwoFactorProviders);
|
||||
// Make sure a new code was generated for the user
|
||||
Assert.NotEqual(recoveryCode, user.TwoFactorRecoveryCode);
|
||||
await sutProvider.GetDependency<IMailService>()
|
||||
.Received(1)
|
||||
.SendRecoverTwoFactorEmail(Arg.Any<string>(), Arg.Any<DateTime>(), Arg.Any<string>());
|
||||
await sutProvider.GetDependency<IEventService>()
|
||||
.Received(1)
|
||||
.LogUserEventAsync(user.Id, EventType.User_Recovered2fa);
|
||||
}
|
||||
|
||||
[Theory, BitAutoData]
|
||||
public async Task RecoverTwoFactorAsync_IncorrectCode_ReturnsFalse(
|
||||
User user, SutProvider<UserService> sutProvider)
|
||||
{
|
||||
// Arrange
|
||||
var recoveryCode = "1234";
|
||||
user.TwoFactorRecoveryCode = "4567";
|
||||
|
||||
// Act
|
||||
var response = await sutProvider.Sut.RecoverTwoFactorAsync(user, recoveryCode);
|
||||
|
||||
// Assert
|
||||
Assert.False(response);
|
||||
Assert.NotNull(user.TwoFactorProviders);
|
||||
}
|
||||
|
||||
private static void SetupUserAndDevice(User user,
|
||||
bool shouldHavePassword)
|
||||
{
|
||||
|
@ -105,7 +105,7 @@ public class BaseRequestValidatorTests
|
||||
// Assert
|
||||
await _eventService.Received(1)
|
||||
.LogUserEventAsync(context.CustomValidatorRequestContext.User.Id,
|
||||
Core.Enums.EventType.User_FailedLogIn);
|
||||
EventType.User_FailedLogIn);
|
||||
Assert.True(context.GrantResult.IsError);
|
||||
Assert.Equal("Username or password is incorrect. Try again.", errorResponse.Message);
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
using Bit.Core.AdminConsole.Entities;
|
||||
using Bit.Core;
|
||||
using Bit.Core.AdminConsole.Entities;
|
||||
using Bit.Core.Auth.Enums;
|
||||
using Bit.Core.Auth.Identity.TokenProviders;
|
||||
using Bit.Core.Auth.Models.Business.Tokenables;
|
||||
@ -328,7 +329,7 @@ public class TwoFactorAuthenticationValidatorTests
|
||||
_userManager.TWO_FACTOR_PROVIDERS = ["email"];
|
||||
|
||||
// Act
|
||||
var result = await _sut.VerifyTwoFactor(
|
||||
var result = await _sut.VerifyTwoFactorAsync(
|
||||
user, null, TwoFactorProviderType.U2f, token);
|
||||
|
||||
// Assert
|
||||
@ -348,7 +349,7 @@ public class TwoFactorAuthenticationValidatorTests
|
||||
_userManager.TWO_FACTOR_PROVIDERS = ["email"];
|
||||
|
||||
// Act
|
||||
var result = await _sut.VerifyTwoFactor(
|
||||
var result = await _sut.VerifyTwoFactorAsync(
|
||||
user, null, TwoFactorProviderType.Email, token);
|
||||
|
||||
// Assert
|
||||
@ -368,7 +369,7 @@ public class TwoFactorAuthenticationValidatorTests
|
||||
_userManager.TWO_FACTOR_PROVIDERS = ["OrganizationDuo"];
|
||||
|
||||
// Act
|
||||
var result = await _sut.VerifyTwoFactor(
|
||||
var result = await _sut.VerifyTwoFactorAsync(
|
||||
user, null, TwoFactorProviderType.OrganizationDuo, token);
|
||||
|
||||
// Assert
|
||||
@ -394,7 +395,7 @@ public class TwoFactorAuthenticationValidatorTests
|
||||
_userManager.TWO_FACTOR_TOKEN_VERIFIED = true;
|
||||
|
||||
// Act
|
||||
var result = await _sut.VerifyTwoFactor(user, null, providerType, token);
|
||||
var result = await _sut.VerifyTwoFactorAsync(user, null, providerType, token);
|
||||
|
||||
// Assert
|
||||
Assert.True(result);
|
||||
@ -419,7 +420,7 @@ public class TwoFactorAuthenticationValidatorTests
|
||||
_userManager.TWO_FACTOR_TOKEN_VERIFIED = false;
|
||||
|
||||
// Act
|
||||
var result = await _sut.VerifyTwoFactor(user, null, providerType, token);
|
||||
var result = await _sut.VerifyTwoFactorAsync(user, null, providerType, token);
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
@ -445,13 +446,56 @@ public class TwoFactorAuthenticationValidatorTests
|
||||
organization.Enabled = true;
|
||||
|
||||
// Act
|
||||
var result = await _sut.VerifyTwoFactor(
|
||||
var result = await _sut.VerifyTwoFactorAsync(
|
||||
user, organization, providerType, token);
|
||||
|
||||
// Assert
|
||||
Assert.True(result);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData(TwoFactorProviderType.RecoveryCode)]
|
||||
public async void VerifyTwoFactorAsync_RecoveryCode_ValidToken_ReturnsTrue(
|
||||
TwoFactorProviderType providerType,
|
||||
User user,
|
||||
Organization organization)
|
||||
{
|
||||
var token = "1234";
|
||||
user.TwoFactorRecoveryCode = token;
|
||||
|
||||
_userService.RecoverTwoFactorAsync(Arg.Is(user), Arg.Is(token)).Returns(true);
|
||||
_featureService.IsEnabled(FeatureFlagKeys.RecoveryCodeLogin).Returns(true);
|
||||
|
||||
// Act
|
||||
var result = await _sut.VerifyTwoFactorAsync(
|
||||
user, organization, providerType, token);
|
||||
|
||||
// Assert
|
||||
Assert.True(result);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[BitAutoData(TwoFactorProviderType.RecoveryCode)]
|
||||
public async void VerifyTwoFactorAsync_RecoveryCode_InvalidToken_ReturnsFalse(
|
||||
TwoFactorProviderType providerType,
|
||||
User user,
|
||||
Organization organization)
|
||||
{
|
||||
// Arrange
|
||||
var token = "1234";
|
||||
user.TwoFactorRecoveryCode = token;
|
||||
|
||||
_userService.RecoverTwoFactorAsync(Arg.Is(user), Arg.Is(token)).Returns(false);
|
||||
_featureService.IsEnabled(FeatureFlagKeys.RecoveryCodeLogin).Returns(true);
|
||||
|
||||
// Act
|
||||
var result = await _sut.VerifyTwoFactorAsync(
|
||||
user, organization, providerType, token);
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
}
|
||||
|
||||
private static UserManagerTestWrapper<User> SubstituteUserManager()
|
||||
{
|
||||
return new UserManagerTestWrapper<User>(
|
||||
|
Reference in New Issue
Block a user