[PM-3393] Excessive Invalid Biometric unlock attempts should automatically log out TDE users (#2747)

* [PM-3393] Log user out on biometric exceed attempts

* [PM-3393] Move duplicated code to AppHelpers

* [PM-3393] Update copy on new pop up

* [PM-3393] Moved VaultTimeoutService to LazyResolve.

* [PM-3382] Change IVaultTimeoutService for messaging

* [PM-3393] Use default values.
This commit is contained in:
André Bispo 2023-09-19 10:32:23 +01:00 committed by GitHub
parent b932824b5a
commit a6f05338c2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 41 additions and 6 deletions

View file

@ -465,7 +465,8 @@ namespace Bit.App.Pages
} }
var success = await _platformUtilsService.AuthenticateBiometricAsync(null, var success = await _platformUtilsService.AuthenticateBiometricAsync(null,
PinEnabled ? AppResources.PIN : AppResources.MasterPassword, PinEnabled ? AppResources.PIN : AppResources.MasterPassword,
() => _secretEntryFocusWeakEventManager.RaiseEvent((int?)null, nameof(FocusSecretEntry))); () => _secretEntryFocusWeakEventManager.RaiseEvent((int?)null, nameof(FocusSecretEntry)),
!PinEnabled && !HasMasterPassword);
await _stateService.SetBiometricLockedAsync(!success); await _stateService.SetBiometricLockedAsync(!success);
if (success) if (success)
{ {

View file

@ -256,6 +256,15 @@ namespace Bit.App.Resources {
} }
} }
/// <summary>
/// Looks up a localized string similar to Account logged out..
/// </summary>
public static string AccountLoggedOutBiometricExceeded {
get {
return ResourceManager.GetString("AccountLoggedOutBiometricExceeded", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Account logged out successfully. /// Looks up a localized string similar to Account logged out successfully.
/// </summary> /// </summary>
@ -6498,6 +6507,15 @@ namespace Bit.App.Resources {
} }
} }
/// <summary>
/// Looks up a localized string similar to Too many attempts.
/// </summary>
public static string TooManyAttempts {
get {
return ResourceManager.GetString("TooManyAttempts", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to TOTP. /// Looks up a localized string similar to TOTP.
/// </summary> /// </summary>
@ -6751,7 +6769,7 @@ namespace Bit.App.Resources {
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to Unlocking may fail due to insufficient memory. Decrease your KDF memory settings to resolve.. /// Looks up a localized string similar to Unlocking may fail due to insufficient memory. Decrease your KDF memory settings or set up biometric unlock to resolve..
/// </summary> /// </summary>
public static string UnlockingMayFailDueToInsufficientMemoryDecreaseYourKDFMemorySettingsToResolve { public static string UnlockingMayFailDueToInsufficientMemoryDecreaseYourKDFMemorySettingsToResolve {
get { get {

View file

@ -2762,4 +2762,10 @@ Do you want to switch to this account?</value>
<data name="LoggingInOn" xml:space="preserve"> <data name="LoggingInOn" xml:space="preserve">
<value>Logging in on</value> <value>Logging in on</value>
</data> </data>
<data name="TooManyAttempts" xml:space="preserve">
<value>Too many attempts</value>
</data>
<data name="AccountLoggedOutBiometricExceeded" xml:space="preserve">
<value>Account logged out.</value>
</data>
</root> </root>

View file

@ -32,7 +32,8 @@ namespace Bit.App.Services
IDeviceActionService deviceActionService, IDeviceActionService deviceActionService,
IClipboardService clipboardService, IClipboardService clipboardService,
IMessagingService messagingService, IMessagingService messagingService,
IBroadcasterService broadcasterService) IBroadcasterService broadcasterService
)
{ {
_deviceActionService = deviceActionService; _deviceActionService = deviceActionService;
_clipboardService = clipboardService; _clipboardService = clipboardService;
@ -242,7 +243,7 @@ namespace Bit.App.Services
} }
public async Task<bool> AuthenticateBiometricAsync(string text = null, string fallbackText = null, public async Task<bool> AuthenticateBiometricAsync(string text = null, string fallbackText = null,
Action fallback = null) Action fallback = null, bool logOutOnTooManyAttempts = false)
{ {
try try
{ {
@ -269,6 +270,12 @@ namespace Bit.App.Services
{ {
fallback?.Invoke(); fallback?.Invoke();
} }
if (result.Status == FingerprintAuthenticationResultStatus.TooManyAttempts
&& logOutOnTooManyAttempts)
{
await ShowDialogAsync(AppResources.AccountLoggedOutBiometricExceeded, AppResources.TooManyAttempts, AppResources.Ok);
_messagingService.Send(AccountsManagerMessageCommands.LOGOUT);
}
} }
catch { } catch { }
return false; return false;

View file

@ -15,6 +15,7 @@ using Bit.Core.Enums;
using Bit.Core.Exceptions; using Bit.Core.Exceptions;
using Bit.Core.Models.Data; using Bit.Core.Models.Data;
using Bit.Core.Models.View; using Bit.Core.Models.View;
using Bit.Core.Services;
using Bit.Core.Utilities; using Bit.Core.Utilities;
using Newtonsoft.Json; using Newtonsoft.Json;
using Xamarin.Essentials; using Xamarin.Essentials;

View file

@ -29,7 +29,7 @@ namespace Bit.Core.Abstractions
bool SupportsDuo(); bool SupportsDuo();
Task<bool> SupportsBiometricAsync(); Task<bool> SupportsBiometricAsync();
Task<bool> IsBiometricIntegrityValidAsync(string bioIntegritySrcKey = null); Task<bool> IsBiometricIntegrityValidAsync(string bioIntegritySrcKey = null);
Task<bool> AuthenticateBiometricAsync(string text = null, string fallbackText = null, Action fallback = null); Task<bool> AuthenticateBiometricAsync(string text = null, string fallbackText = null, Action fallback = null, bool logOutOnTooManyAttempts = false);
long GetActiveTime(); long GetActiveTime();
} }
} }

View file

@ -378,7 +378,9 @@ namespace Bit.iOS.Core.Controllers
} }
var success = await _platformUtilsService.AuthenticateBiometricAsync(null, var success = await _platformUtilsService.AuthenticateBiometricAsync(null,
_pinEnabled ? AppResources.PIN : AppResources.MasterPassword, _pinEnabled ? AppResources.PIN : AppResources.MasterPassword,
() => MasterPasswordCell.TextField.BecomeFirstResponder()); () => MasterPasswordCell.TextField.BecomeFirstResponder(),
!_pinEnabled && !_hasMasterPassword);
await _stateService.SetBiometricLockedAsync(!success); await _stateService.SetBiometricLockedAsync(!success);
if (success) if (success)
{ {