mirror of
https://github.com/bitwarden/mobile.git
synced 2024-12-27 17:08:00 +01:00
biometrics cleanup (#964)
This commit is contained in:
parent
ec7d87e757
commit
5da2f3279b
@ -8,12 +8,9 @@ using Android.App;
|
||||
using Android.App.Assist;
|
||||
using Android.Content;
|
||||
using Android.Content.PM;
|
||||
using Android.Hardware.Biometrics;
|
||||
using Android.Hardware.Fingerprints;
|
||||
using Android.Nfc;
|
||||
using Android.OS;
|
||||
using Android.Provider;
|
||||
using Android.Runtime;
|
||||
using Android.Text;
|
||||
using Android.Text.Method;
|
||||
using Android.Views.Autofill;
|
||||
@ -31,7 +28,6 @@ using Bit.Core.Models.View;
|
||||
using Bit.Core.Utilities;
|
||||
using Bit.Droid.Autofill;
|
||||
using Plugin.CurrentActivity;
|
||||
using Plugin.Fingerprint;
|
||||
|
||||
namespace Bit.Droid.Services
|
||||
{
|
||||
@ -389,71 +385,16 @@ namespace Bit.Droid.Services
|
||||
|
||||
public bool SupportsFaceBiometric()
|
||||
{
|
||||
// only used by iOS
|
||||
return false;
|
||||
}
|
||||
|
||||
public Task<bool> SupportsFaceBiometricAsync()
|
||||
{
|
||||
// only used by iOS
|
||||
return Task.FromResult(SupportsFaceBiometric());
|
||||
}
|
||||
|
||||
public async Task<bool> BiometricAvailableAsync()
|
||||
{
|
||||
if (UseNativeBiometric())
|
||||
{
|
||||
var activity = (MainActivity)CrossCurrentActivity.Current.Activity;
|
||||
var manager = activity.GetSystemService(Context.BiometricService) as BiometricManager;
|
||||
return manager.CanAuthenticate() == BiometricCode.Success;
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
return await CrossFingerprint.Current.IsAvailableAsync();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool UseNativeBiometric()
|
||||
{
|
||||
return (int)Build.VERSION.SdkInt >= 29;
|
||||
}
|
||||
|
||||
public Task<bool> AuthenticateBiometricAsync(string text = null)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(text))
|
||||
{
|
||||
text = AppResources.BiometricsDirection;
|
||||
}
|
||||
|
||||
var activity = (MainActivity)CrossCurrentActivity.Current.Activity;
|
||||
using (var builder = new BiometricPrompt.Builder(activity))
|
||||
{
|
||||
builder.SetTitle(text);
|
||||
builder.SetConfirmationRequired(false);
|
||||
builder.SetNegativeButton(AppResources.Cancel, activity.MainExecutor,
|
||||
new DialogInterfaceOnClickListener
|
||||
{
|
||||
Clicked = () => { }
|
||||
});
|
||||
var prompt = builder.Build();
|
||||
var result = new TaskCompletionSource<bool>();
|
||||
prompt.Authenticate(new CancellationSignal(), activity.MainExecutor,
|
||||
new BiometricAuthenticationCallback
|
||||
{
|
||||
Success = authResult => result.TrySetResult(true),
|
||||
Error = () => result.TrySetResult(false),
|
||||
Failed = () => { },
|
||||
Help = (helpCode, helpString) => { }
|
||||
});
|
||||
return result.Task;
|
||||
}
|
||||
}
|
||||
|
||||
public bool SupportsNfc()
|
||||
{
|
||||
var activity = (MainActivity)CrossCurrentActivity.Current.Activity;
|
||||
@ -870,48 +811,5 @@ namespace Bit.Droid.Services
|
||||
Context.ClipboardService) as Android.Content.ClipboardManager;
|
||||
clipboardManager.PrimaryClip = ClipData.NewPlainText("bitwarden", text);
|
||||
}
|
||||
|
||||
private class BiometricAuthenticationCallback : BiometricPrompt.AuthenticationCallback
|
||||
{
|
||||
public Action<BiometricPrompt.AuthenticationResult> Success { get; set; }
|
||||
public Action Error { get; set; }
|
||||
public Action Failed { get; set; }
|
||||
public Action<BiometricAcquiredStatus, Java.Lang.ICharSequence> Help { get; set; }
|
||||
|
||||
public override void OnAuthenticationSucceeded(BiometricPrompt.AuthenticationResult authResult)
|
||||
{
|
||||
base.OnAuthenticationSucceeded(authResult);
|
||||
Success?.Invoke(authResult);
|
||||
}
|
||||
|
||||
public override void OnAuthenticationError([GeneratedEnum] BiometricErrorCode errorCode, Java.Lang.ICharSequence errString)
|
||||
{
|
||||
base.OnAuthenticationError(errorCode, errString);
|
||||
Error?.Invoke();
|
||||
}
|
||||
|
||||
public override void OnAuthenticationFailed()
|
||||
{
|
||||
base.OnAuthenticationFailed();
|
||||
Failed?.Invoke();
|
||||
}
|
||||
|
||||
public override void OnAuthenticationHelp([GeneratedEnum] BiometricAcquiredStatus helpCode,
|
||||
Java.Lang.ICharSequence helpString)
|
||||
{
|
||||
base.OnAuthenticationHelp(helpCode, helpString);
|
||||
Help?.Invoke(helpCode, helpString);
|
||||
}
|
||||
}
|
||||
|
||||
private class DialogInterfaceOnClickListener : Java.Lang.Object, IDialogInterfaceOnClickListener
|
||||
{
|
||||
public Action Clicked { get; set; }
|
||||
|
||||
public void OnClick(IDialogInterface dialog, int which)
|
||||
{
|
||||
Clicked?.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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");
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
1
src/App/Resources/AppResources.Designer.cs
generated
1
src/App/Resources/AppResources.Designer.cs
generated
@ -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")]
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,13 +7,13 @@ namespace Bit.Core.Abstractions
|
||||
public interface IVaultTimeoutService
|
||||
{
|
||||
CipherString PinProtectedKey { get; set; }
|
||||
bool FingerprintLocked { get; set; }
|
||||
bool BiometricLocked { get; set; }
|
||||
|
||||
Task CheckVaultTimeoutAsync();
|
||||
Task ClearAsync();
|
||||
Task<bool> IsLockedAsync();
|
||||
Task<Tuple<bool, bool>> IsPinLockSetAsync();
|
||||
Task<bool> IsFingerprintLockSetAsync();
|
||||
Task<bool> IsBiometricLockSetAsync();
|
||||
Task LockAsync(bool allowSoftLock = false, bool userInitiated = false);
|
||||
Task LogOutAsync();
|
||||
Task SetVaultTimeoutOptionsAsync(int? timeout, string action);
|
||||
|
@ -7,7 +7,7 @@
|
||||
public static string VaultTimeoutKey = "lockOption";
|
||||
public static string VaultTimeoutActionKey = "vaultTimeoutAction";
|
||||
public static string LastActiveKey = "lastActive";
|
||||
public static string FingerprintUnlockKey = "fingerprintUnlock";
|
||||
public static string BiometricUnlockKey = "fingerprintUnlock";
|
||||
public static string ProtectedPin = "protectedPin";
|
||||
public static string PinProtectedKey = "pinProtectedKey";
|
||||
public static string DefaultUriMatch = "defaultUriMatch";
|
||||
|
@ -315,7 +315,7 @@ namespace Bit.Core.Services
|
||||
await _cryptoService.SetEncPrivateKeyAsync(tokenResponse.PrivateKey);
|
||||
}
|
||||
|
||||
_vaultTimeoutService.FingerprintLocked = false;
|
||||
_vaultTimeoutService.BiometricLocked = false;
|
||||
_messagingService.Send("loggedIn");
|
||||
return result;
|
||||
}
|
||||
|
@ -48,8 +48,8 @@ namespace Bit.Core.Services
|
||||
{
|
||||
_key = key;
|
||||
var option = await _storageService.GetAsync<int?>(Constants.VaultTimeoutKey);
|
||||
var fingerprint = await _storageService.GetAsync<bool?>(Constants.FingerprintUnlockKey);
|
||||
if (option.HasValue && !fingerprint.GetValueOrDefault())
|
||||
var biometric = await _storageService.GetAsync<bool?>(Constants.BiometricUnlockKey);
|
||||
if (option.HasValue && !biometric.GetValueOrDefault())
|
||||
{
|
||||
// If we have a lock option set, we do not store the key
|
||||
return;
|
||||
@ -354,8 +354,8 @@ namespace Bit.Core.Services
|
||||
{
|
||||
var key = await GetKeyAsync();
|
||||
var option = await _storageService.GetAsync<int?>(Constants.VaultTimeoutKey);
|
||||
var fingerprint = await _storageService.GetAsync<bool?>(Constants.FingerprintUnlockKey);
|
||||
if (!fingerprint.GetValueOrDefault() && (option != null || option == 0))
|
||||
var biometric = await _storageService.GetAsync<bool?>(Constants.BiometricUnlockKey);
|
||||
if (!biometric.GetValueOrDefault() && (option != null || option == 0))
|
||||
{
|
||||
await ClearKeyAsync();
|
||||
_key = key;
|
||||
|
@ -49,15 +49,15 @@ namespace Bit.Core.Services
|
||||
}
|
||||
|
||||
public CipherString PinProtectedKey { get; set; } = null;
|
||||
public bool FingerprintLocked { get; set; } = true;
|
||||
public bool BiometricLocked { get; set; } = true;
|
||||
|
||||
public async Task<bool> IsLockedAsync()
|
||||
{
|
||||
var hasKey = await _cryptoService.HasKeyAsync();
|
||||
if (hasKey)
|
||||
{
|
||||
var fingerprintSet = await IsFingerprintLockSetAsync();
|
||||
if (fingerprintSet && FingerprintLocked)
|
||||
var biometricSet = await IsBiometricLockSetAsync();
|
||||
if (biometricSet && BiometricLocked)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@ -120,8 +120,8 @@ namespace Bit.Core.Services
|
||||
}
|
||||
if (allowSoftLock)
|
||||
{
|
||||
FingerprintLocked = await IsFingerprintLockSetAsync();
|
||||
if (FingerprintLocked)
|
||||
BiometricLocked = await IsBiometricLockSetAsync();
|
||||
if (BiometricLocked)
|
||||
{
|
||||
_messagingService.Send("locked", userInitiated);
|
||||
_lockedCallback?.Invoke(userInitiated);
|
||||
@ -165,10 +165,10 @@ namespace Bit.Core.Services
|
||||
return new Tuple<bool, bool>(protectedPin != null, pinProtectedKey != null);
|
||||
}
|
||||
|
||||
public async Task<bool> IsFingerprintLockSetAsync()
|
||||
public async Task<bool> IsBiometricLockSetAsync()
|
||||
{
|
||||
var fingerprintLock = await _storageService.GetAsync<bool?>(Constants.FingerprintUnlockKey);
|
||||
return fingerprintLock.GetValueOrDefault();
|
||||
var biometricLock = await _storageService.GetAsync<bool?>(Constants.BiometricUnlockKey);
|
||||
return biometricLock.GetValueOrDefault();
|
||||
}
|
||||
|
||||
public async Task ClearAsync()
|
||||
|
@ -52,7 +52,7 @@ namespace Bit.iOS.Core.Controllers
|
||||
|
||||
_pinSet = _vaultTimeoutService.IsPinLockSetAsync().GetAwaiter().GetResult();
|
||||
_pinLock = (_pinSet.Item1 && _vaultTimeoutService.PinProtectedKey != null) || _pinSet.Item2;
|
||||
_fingerprintLock = _vaultTimeoutService.IsFingerprintLockSetAsync().GetAwaiter().GetResult();
|
||||
_fingerprintLock = _vaultTimeoutService.IsBiometricLockSetAsync().GetAwaiter().GetResult();
|
||||
|
||||
BaseNavItem.Title = _pinLock ? AppResources.VerifyPIN : AppResources.VerifyMasterPassword;
|
||||
BaseCancelButton.Title = AppResources.Cancel;
|
||||
@ -205,7 +205,7 @@ namespace Bit.iOS.Core.Controllers
|
||||
|
||||
private void DoContinue()
|
||||
{
|
||||
_vaultTimeoutService.FingerprintLocked = false;
|
||||
_vaultTimeoutService.BiometricLocked = false;
|
||||
MasterPasswordCell.TextField.ResignFirstResponder();
|
||||
Success();
|
||||
}
|
||||
@ -219,7 +219,7 @@ namespace Bit.iOS.Core.Controllers
|
||||
var success = await _platformUtilsService.AuthenticateBiometricAsync(null,
|
||||
_pinLock ? AppResources.PIN : AppResources.MasterPassword,
|
||||
() => MasterPasswordCell.TextField.BecomeFirstResponder());
|
||||
_vaultTimeoutService.FingerprintLocked = !success;
|
||||
_vaultTimeoutService.BiometricLocked = !success;
|
||||
if (success)
|
||||
{
|
||||
DoContinue();
|
||||
@ -261,11 +261,11 @@ namespace Bit.iOS.Core.Controllers
|
||||
{
|
||||
if (indexPath.Row == 0)
|
||||
{
|
||||
var fingerprintButtonText = _controller._deviceActionService.SupportsFaceBiometric() ?
|
||||
var biometricButtonText = _controller._deviceActionService.SupportsFaceBiometric() ?
|
||||
AppResources.UseFaceIDToUnlock : AppResources.UseFingerprintToUnlock;
|
||||
var cell = new ExtendedUITableViewCell();
|
||||
cell.TextLabel.TextColor = ThemeHelpers.PrimaryColor;
|
||||
cell.TextLabel.Text = fingerprintButtonText;
|
||||
cell.TextLabel.Text = biometricButtonText;
|
||||
return cell;
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,6 @@ using Foundation;
|
||||
using LocalAuthentication;
|
||||
using MobileCoreServices;
|
||||
using Photos;
|
||||
using Plugin.Fingerprint;
|
||||
using UIKit;
|
||||
using Xamarin.Forms;
|
||||
|
||||
@ -271,28 +270,6 @@ namespace Bit.iOS.Core.Services
|
||||
return Task.FromResult(SupportsFaceBiometric());
|
||||
}
|
||||
|
||||
public async Task<bool> BiometricAvailableAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
return await CrossFingerprint.Current.IsAvailableAsync();
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public bool UseNativeBiometric()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public Task<bool> AuthenticateBiometricAsync(string text = null)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public bool SupportsNfc()
|
||||
{
|
||||
if(Application.Current is App.App currentApp && !currentApp.Options.IosExtension)
|
||||
|
Loading…
Reference in New Issue
Block a user