1
0
mirror of https://github.com/bitwarden/server.git synced 2025-07-02 00:22:50 -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:
Patrick-Pimentel-Bitwarden
2025-02-13 15:51:36 -05:00
committed by GitHub
parent 465549b812
commit ac6bc40d85
10 changed files with 220 additions and 76 deletions

View File

@ -315,7 +315,7 @@ public class UserService : UserManager<User>, IUserService, IDisposable
return;
}
var token = await base.GenerateUserTokenAsync(user, TokenOptions.DefaultProvider, "DeleteAccount");
var token = await GenerateUserTokenAsync(user, TokenOptions.DefaultProvider, "DeleteAccount");
await _mailService.SendVerifyDeleteEmailAsync(user.Email, user.Id, token);
}
@ -868,6 +868,10 @@ public class UserService : UserManager<User>, IUserService, IDisposable
}
}
/// <summary>
/// To be removed when the feature flag pm-17128-recovery-code-login is removed PM-18175.
/// </summary>
[Obsolete("Two Factor recovery is handled in the TwoFactorAuthenticationValidator.")]
public async Task<bool> RecoverTwoFactorAsync(string email, string secret, string recoveryCode)
{
var user = await _userRepository.GetByEmailAsync(email);
@ -897,6 +901,25 @@ public class UserService : UserManager<User>, IUserService, IDisposable
return true;
}
public async Task<bool> RecoverTwoFactorAsync(User user, string recoveryCode)
{
if (!CoreHelpers.FixedTimeEquals(
user.TwoFactorRecoveryCode,
recoveryCode.Replace(" ", string.Empty).Trim().ToLower()))
{
return false;
}
user.TwoFactorProviders = null;
user.TwoFactorRecoveryCode = CoreHelpers.SecureRandomString(32, upper: false, special: false);
await SaveUserAsync(user);
await _mailService.SendRecoverTwoFactorEmail(user.Email, DateTime.UtcNow, _currentContext.IpAddress);
await _eventService.LogUserEventAsync(user.Id, EventType.User_Recovered2fa);
await CheckPoliciesOnTwoFactorRemovalAsync(user);
return true;
}
public async Task<Tuple<bool, string>> SignUpPremiumAsync(User user, string paymentToken,
PaymentMethodType paymentMethodType, short additionalStorageGb, UserLicense license,
TaxInfo taxInfo)
@ -1081,7 +1104,7 @@ public class UserService : UserManager<User>, IUserService, IDisposable
await EnablePremiumAsync(user, expirationDate);
}
public async Task EnablePremiumAsync(User user, DateTime? expirationDate)
private async Task EnablePremiumAsync(User user, DateTime? expirationDate)
{
if (user != null && !user.Premium && user.Gateway.HasValue)
{
@ -1098,7 +1121,7 @@ public class UserService : UserManager<User>, IUserService, IDisposable
await DisablePremiumAsync(user, expirationDate);
}
public async Task DisablePremiumAsync(User user, DateTime? expirationDate)
private async Task DisablePremiumAsync(User user, DateTime? expirationDate)
{
if (user != null && user.Premium)
{