1
0
mirror of https://github.com/bitwarden/mobile synced 2026-01-03 17:13:50 +00:00

biometrics cleanup (#964)

This commit is contained in:
Matt Portune
2020-06-08 08:25:13 -04:00
committed by GitHub
parent ec7d87e757
commit 5da2f3279b
18 changed files with 117 additions and 257 deletions

View File

@@ -23,9 +23,6 @@ namespace Bit.App.Abstractions
void RateApp();
bool SupportsFaceBiometric();
Task<bool> SupportsFaceBiometricAsync();
Task<bool> BiometricAvailableAsync();
bool UseNativeBiometric();
Task<bool> AuthenticateBiometricAsync(string text = null);
bool SupportsNfc();
bool SupportsCamera();
bool SupportsAutofillService();

View File

@@ -219,7 +219,7 @@ namespace Bit.App
SyncIfNeeded();
if (Current.MainPage is NavigationPage navPage && navPage.CurrentPage is LockPage lockPage)
{
await lockPage.PromptFingerprintAfterResumeAsync();
await lockPage.PromptBiometricAfterResumeAsync();
}
}
@@ -246,7 +246,7 @@ namespace Bit.App
_passwordGenerationService.ClearAsync(),
_vaultTimeoutService.ClearAsync(),
_stateService.PurgeAsync());
_vaultTimeoutService.FingerprintLocked = true;
_vaultTimeoutService.BiometricLocked = true;
_searchService.ClearIndex();
_authService.LogOut(() =>
{
@@ -404,21 +404,20 @@ namespace Bit.App
}
}
private async Task LockedAsync(bool autoPromptFingerprint)
private async Task LockedAsync(bool autoPromptBiometric)
{
await _stateService.PurgeAsync();
if (autoPromptFingerprint && Device.RuntimePlatform == Device.iOS)
if (autoPromptBiometric && Device.RuntimePlatform == Device.iOS)
{
var vaultTimeout = await _storageService.GetAsync<int?>(Constants.VaultTimeoutKey);
if (vaultTimeout == 0)
{
autoPromptFingerprint = false;
autoPromptBiometric = false;
}
}
else if (autoPromptFingerprint && Device.RuntimePlatform == Device.Android &&
_deviceActionService.UseNativeBiometric())
else if (autoPromptBiometric && Device.RuntimePlatform == Device.Android)
{
autoPromptFingerprint = false;
autoPromptBiometric = false;
}
PreviousPageInfo lastPageBeforeLock = null;
if (Current.MainPage is TabbedPage tabbedPage && tabbedPage.Navigation.ModalStack.Count > 0)
@@ -445,7 +444,7 @@ namespace Bit.App
}
}
await _storageService.SaveAsync(Constants.PreviousPageKey, lastPageBeforeLock);
var lockPage = new LockPage(Options, autoPromptFingerprint);
var lockPage = new LockPage(Options, autoPromptBiometric);
Device.BeginInvokeOnMainThread(() => Current.MainPage = new NavigationPage(lockPage));
}
}

View File

@@ -106,8 +106,8 @@
Margin="0, 10, 0, 0" />
</StackLayout>
<StackLayout Padding="10, 0">
<Button Text="{Binding FingerprintButtonText}" Clicked="Fingerprint_Clicked"
IsVisible="{Binding FingerprintLock}"></Button>
<Button Text="{Binding BiometricButtonText}" Clicked="Biometric_Clicked"
IsVisible="{Binding BiometricLock}"></Button>
<Button Text="{u:I18n LogOut}" Clicked="LogOut_Clicked"></Button>
</StackLayout>
</StackLayout>

View File

@@ -12,17 +12,17 @@ namespace Bit.App.Pages
{
private readonly IStorageService _storageService;
private readonly AppOptions _appOptions;
private readonly bool _autoPromptFingerprint;
private readonly bool _autoPromptBiometric;
private readonly LockPageViewModel _vm;
private bool _promptedAfterResume;
private bool _appeared;
public LockPage(AppOptions appOptions = null, bool autoPromptFingerprint = true)
public LockPage(AppOptions appOptions = null, bool autoPromptBiometric = true)
{
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
_appOptions = appOptions;
_autoPromptFingerprint = autoPromptFingerprint;
_autoPromptBiometric = autoPromptBiometric;
InitializeComponent();
_vm = BindingContext as LockPageViewModel;
_vm.Page = this;
@@ -34,15 +34,15 @@ namespace Bit.App.Pages
public Entry MasterPasswordEntry { get; set; }
public Entry PinEntry { get; set; }
public async Task PromptFingerprintAfterResumeAsync()
public async Task PromptBiometricAfterResumeAsync()
{
if (_vm.FingerprintLock)
if (_vm.BiometricLock)
{
await Task.Delay(500);
if (!_promptedAfterResume)
{
_promptedAfterResume = true;
await _vm?.PromptFingerprintAsync();
await _vm?.PromptBiometricAsync();
}
}
}
@@ -55,8 +55,8 @@ namespace Bit.App.Pages
return;
}
_appeared = true;
await _vm.InitAsync(_autoPromptFingerprint);
if (!_vm.FingerprintLock)
await _vm.InitAsync(_autoPromptBiometric);
if (!_vm.BiometricLock)
{
if (_vm.PinLock)
{
@@ -89,11 +89,11 @@ namespace Bit.App.Pages
}
}
private async void Fingerprint_Clicked(object sender, EventArgs e)
private async void Biometric_Clicked(object sender, EventArgs e)
{
if (DoOnce())
{
await _vm.PromptFingerprintAsync();
await _vm.PromptBiometricAsync();
}
}

View File

@@ -28,8 +28,8 @@ namespace Bit.App.Pages
private string _email;
private bool _showPassword;
private bool _pinLock;
private bool _fingerprintLock;
private string _fingerprintButtonText;
private bool _biometricLock;
private string _biometricButtonText;
private string _loggedInAsText;
private string _lockedVerifyText;
private int _invalidPinAttempts = 0;
@@ -69,16 +69,16 @@ namespace Bit.App.Pages
set => SetProperty(ref _pinLock, value);
}
public bool FingerprintLock
public bool BiometricLock
{
get => _fingerprintLock;
set => SetProperty(ref _fingerprintLock, value);
get => _biometricLock;
set => SetProperty(ref _biometricLock, value);
}
public string FingerprintButtonText
public string BiometricButtonText
{
get => _fingerprintButtonText;
set => SetProperty(ref _fingerprintButtonText, value);
get => _biometricButtonText;
set => SetProperty(ref _biometricButtonText, value);
}
public string LoggedInAsText
@@ -100,11 +100,11 @@ namespace Bit.App.Pages
public string Pin { get; set; }
public Action UnlockedAction { get; set; }
public async Task InitAsync(bool autoPromptFingerprint)
public async Task InitAsync(bool autoPromptBiometric)
{
_pinSet = await _vaultTimeoutService.IsPinLockSetAsync();
PinLock = (_pinSet.Item1 && _vaultTimeoutService.PinProtectedKey != null) || _pinSet.Item2;
FingerprintLock = await _vaultTimeoutService.IsFingerprintLockSetAsync();
BiometricLock = await _vaultTimeoutService.IsBiometricLockSetAsync();
_email = await _userService.GetEmailAsync();
var webVault = _environmentService.GetWebVaultUrl();
if (string.IsNullOrWhiteSpace(webVault))
@@ -124,27 +124,21 @@ namespace Bit.App.Pages
LockedVerifyText = AppResources.VaultLockedMasterPassword;
}
if (FingerprintLock)
if (BiometricLock)
{
var supportsFace = await _deviceActionService.SupportsFaceBiometricAsync();
if (Device.RuntimePlatform == Device.iOS && supportsFace)
BiometricButtonText = AppResources.UseBiometricsToUnlock;
if (Device.RuntimePlatform == Device.iOS)
{
FingerprintButtonText = AppResources.UseFaceIDToUnlock;
var supportsFace = await _deviceActionService.SupportsFaceBiometricAsync();
BiometricButtonText = supportsFace ? AppResources.UseFaceIDToUnlock :
AppResources.UseFingerprintToUnlock;
}
else if (Device.RuntimePlatform == Device.Android && _deviceActionService.UseNativeBiometric())
{
FingerprintButtonText = AppResources.UseBiometricsToUnlock;
}
else
{
FingerprintButtonText = AppResources.UseFingerprintToUnlock;
}
if (autoPromptFingerprint)
if (autoPromptBiometric)
{
var tasks = Task.Run(async () =>
{
await Task.Delay(500);
Device.BeginInvokeOnMainThread(async () => await PromptFingerprintAsync());
Device.BeginInvokeOnMainThread(async () => await PromptBiometricAsync());
});
}
}
@@ -271,9 +265,9 @@ namespace Bit.App.Pages
entry.Focus();
}
public async Task PromptFingerprintAsync()
public async Task PromptBiometricAsync()
{
if (!FingerprintLock)
if (!BiometricLock)
{
return;
}
@@ -290,7 +284,7 @@ namespace Bit.App.Pages
page.MasterPasswordEntry.Focus();
}
});
_vaultTimeoutService.FingerprintLocked = !success;
_vaultTimeoutService.BiometricLocked = !success;
if (success)
{
await DoContinueAsync();
@@ -309,7 +303,7 @@ namespace Bit.App.Pages
private async Task DoContinueAsync()
{
_vaultTimeoutService.FingerprintLocked = false;
_vaultTimeoutService.BiometricLocked = false;
var disableFavicon = await _storageService.GetAsync<bool?>(Constants.DisableFaviconKey);
await _stateService.SaveAsync(Constants.DisableFaviconKey, disableFavicon.GetValueOrDefault());
_messagingService.Send("unlocked");

View File

@@ -143,19 +143,15 @@ namespace Bit.App.Pages
}
else
{
var fingerprintName = AppResources.Fingerprint;
var biometricName = AppResources.Biometrics;
if (Device.RuntimePlatform == Device.iOS)
{
var supportsFace = await _deviceActionService.SupportsFaceBiometricAsync();
fingerprintName = supportsFace ? AppResources.FaceID : AppResources.TouchID;
biometricName = supportsFace ? AppResources.FaceID : AppResources.TouchID;
}
else if (Device.RuntimePlatform == Device.Android && _deviceActionService.UseNativeBiometric())
if (item.Name == string.Format(AppResources.UnlockWith, biometricName))
{
fingerprintName = AppResources.Biometrics;
}
if (item.Name == string.Format(AppResources.UnlockWith, fingerprintName))
{
await _vm.UpdateFingerprintAsync();
await _vm.UpdateBiometricAsync();
}
}
}

View File

@@ -23,9 +23,9 @@ namespace Bit.App.Pages
private readonly IStorageService _storageService;
private readonly ISyncService _syncService;
private bool _supportsFingerprint;
private bool _supportsBiometric;
private bool _pin;
private bool _fingerprint;
private bool _biometric;
private string _lastSyncDate;
private string _vaultTimeoutDisplayValue;
private string _vaultTimeoutActionDisplayValue;
@@ -69,7 +69,7 @@ namespace Bit.App.Pages
public async Task InitAsync()
{
_supportsFingerprint = await _platformUtilsService.SupportsBiometricAsync();
_supportsBiometric = await _platformUtilsService.SupportsBiometricAsync();
var lastSync = await _syncService.GetLastSyncAsync();
if (lastSync != null)
{
@@ -83,7 +83,7 @@ namespace Bit.App.Pages
_vaultTimeoutActionDisplayValue = _vaultTimeoutActions.FirstOrDefault(o => o.Value == action).Key;
var pinSet = await _vaultTimeoutService.IsPinLockSetAsync();
_pin = pinSet.Item1 || pinSet.Item2;
_fingerprint = await _vaultTimeoutService.IsFingerprintLockSetAsync();
_biometric = await _vaultTimeoutService.IsBiometricLockSetAsync();
BuildList();
}
@@ -288,31 +288,31 @@ namespace Bit.App.Pages
BuildList();
}
public async Task UpdateFingerprintAsync()
public async Task UpdateBiometricAsync()
{
var current = _fingerprint;
if (_fingerprint)
var current = _biometric;
if (_biometric)
{
_fingerprint = false;
_biometric = false;
}
else if (await _platformUtilsService.SupportsBiometricAsync())
{
_fingerprint = await _platformUtilsService.AuthenticateBiometricAsync(null,
_biometric = await _platformUtilsService.AuthenticateBiometricAsync(null,
_deviceActionService.DeviceType == Core.Enums.DeviceType.Android ? "." : null);
}
if (_fingerprint == current)
if (_biometric == current)
{
return;
}
if (_fingerprint)
if (_biometric)
{
await _storageService.SaveAsync(Constants.FingerprintUnlockKey, true);
await _storageService.SaveAsync(Constants.BiometricUnlockKey, true);
}
else
{
await _storageService.RemoveAsync(Constants.FingerprintUnlockKey);
await _storageService.RemoveAsync(Constants.BiometricUnlockKey);
}
_vaultTimeoutService.FingerprintLocked = false;
_vaultTimeoutService.BiometricLocked = false;
await _cryptoService.ToggleKeyAsync();
BuildList();
}
@@ -371,22 +371,18 @@ namespace Bit.App.Pages
new SettingsPageListItem { Name = AppResources.LockNow },
new SettingsPageListItem { Name = AppResources.TwoStepLogin }
};
if (_supportsFingerprint || _fingerprint)
if (_supportsBiometric || _biometric)
{
var fingerprintName = AppResources.Fingerprint;
var biometricName = AppResources.Biometrics;
if (Device.RuntimePlatform == Device.iOS)
{
fingerprintName = _deviceActionService.SupportsFaceBiometric() ? AppResources.FaceID :
biometricName = _deviceActionService.SupportsFaceBiometric() ? AppResources.FaceID :
AppResources.TouchID;
}
else if (Device.RuntimePlatform == Device.Android && _deviceActionService.UseNativeBiometric())
{
fingerprintName = AppResources.Biometrics;
}
var item = new SettingsPageListItem
{
Name = string.Format(AppResources.UnlockWith, fingerprintName),
SubLabel = _fingerprint ? AppResources.Enabled : AppResources.Disabled
Name = string.Format(AppResources.UnlockWith, biometricName),
SubLabel = _biometric ? AppResources.Enabled : AppResources.Disabled
};
securityItems.Insert(2, item);
}

View File

@@ -9,7 +9,6 @@
namespace Bit.App.Resources {
using System;
using System.Reflection;
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]

View File

@@ -1564,7 +1564,7 @@
<value>Your login session has expired.</value>
</data>
<data name="BiometricsDirection" xml:space="preserve">
<value>Use biometrics to verify.</value>
<value>Biometric Verification</value>
</data>
<data name="Biometrics" xml:space="preserve">
<value>Biometrics</value>

View File

@@ -199,43 +199,47 @@ namespace Bit.App.Services
public async Task<bool> SupportsBiometricAsync()
{
return await _deviceActionService.BiometricAvailableAsync();
try
{
return await CrossFingerprint.Current.IsAvailableAsync();
}
catch
{
return false;
}
}
public async Task<bool> AuthenticateBiometricAsync(string text = null, string fallbackText = null,
Action fallback = null)
{
if (_deviceActionService.UseNativeBiometric())
try
{
return await _deviceActionService.AuthenticateBiometricAsync(text);
}
else
{
try
if (text == null)
{
if (text == null)
text = AppResources.BiometricsDirection;
if (Device.RuntimePlatform == Device.iOS)
{
var supportsFace = await _deviceActionService.SupportsFaceBiometricAsync();
text = supportsFace ? AppResources.FaceIDDirection : AppResources.FingerprintDirection;
}
var fingerprintRequest = new AuthenticationRequestConfiguration(text, text)
{
CancelTitle = AppResources.Cancel,
FallbackTitle = fallbackText
};
var result = await CrossFingerprint.Current.AuthenticateAsync(fingerprintRequest);
if (result.Authenticated)
{
return true;
}
else if (result.Status == FingerprintAuthenticationResultStatus.FallbackRequested)
{
fallback?.Invoke();
}
}
catch { }
return false;
var biometricRequest = new AuthenticationRequestConfiguration(AppResources.Bitwarden, text)
{
CancelTitle = AppResources.Cancel,
FallbackTitle = fallbackText
};
var result = await CrossFingerprint.Current.AuthenticateAsync(biometricRequest);
if (result.Authenticated)
{
return true;
}
if (result.Status == FingerprintAuthenticationResultStatus.FallbackRequested)
{
fallback?.Invoke();
}
}
catch { }
return false;
}
}
}