mirror of
https://github.com/bitwarden/mobile.git
synced 2024-12-25 16:47:55 +01:00
fingerprint locking
This commit is contained in:
parent
22a0b262e1
commit
5761b47073
@ -147,6 +147,7 @@ namespace Bit.App
|
|||||||
_passwordGenerationService.ClearAsync(),
|
_passwordGenerationService.ClearAsync(),
|
||||||
_lockService.ClearAsync());
|
_lockService.ClearAsync());
|
||||||
_lockService.PinLocked = false;
|
_lockService.PinLocked = false;
|
||||||
|
_lockService.FingerprintLocked = true;
|
||||||
_searchService.ClearIndex();
|
_searchService.ClearIndex();
|
||||||
_authService.LogOut(() =>
|
_authService.LogOut(() =>
|
||||||
{
|
{
|
||||||
|
@ -28,7 +28,7 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
RequestFocus(PinEntry);
|
RequestFocus(PinEntry);
|
||||||
}
|
}
|
||||||
else
|
else if(!_vm.FingerprintLock)
|
||||||
{
|
{
|
||||||
RequestFocus(MasterPasswordEntry);
|
RequestFocus(MasterPasswordEntry);
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,7 @@ namespace Bit.App.Pages
|
|||||||
private string _email;
|
private string _email;
|
||||||
private bool _showPassword;
|
private bool _showPassword;
|
||||||
private bool _pinLock;
|
private bool _pinLock;
|
||||||
|
private bool _fingerprintLock;
|
||||||
private int _invalidPinAttempts = 0;
|
private int _invalidPinAttempts = 0;
|
||||||
private Tuple<bool, bool> _pinSet;
|
private Tuple<bool, bool> _pinSet;
|
||||||
|
|
||||||
@ -57,6 +58,12 @@ namespace Bit.App.Pages
|
|||||||
set => SetProperty(ref _pinLock, value);
|
set => SetProperty(ref _pinLock, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool FingerprintLock
|
||||||
|
{
|
||||||
|
get => _fingerprintLock;
|
||||||
|
set => SetProperty(ref _fingerprintLock, value);
|
||||||
|
}
|
||||||
|
|
||||||
public Command TogglePasswordCommand { get; }
|
public Command TogglePasswordCommand { get; }
|
||||||
public string ShowPasswordIcon => ShowPassword ? "" : "";
|
public string ShowPasswordIcon => ShowPassword ? "" : "";
|
||||||
public string MasterPassword { get; set; }
|
public string MasterPassword { get; set; }
|
||||||
@ -67,8 +74,25 @@ namespace Bit.App.Pages
|
|||||||
_pinSet = await _lockService.IsPinLockSetAsync();
|
_pinSet = await _lockService.IsPinLockSetAsync();
|
||||||
var hasKey = await _cryptoService.HasKeyAsync();
|
var hasKey = await _cryptoService.HasKeyAsync();
|
||||||
PinLock = (_pinSet.Item1 && hasKey) || _pinSet.Item2;
|
PinLock = (_pinSet.Item1 && hasKey) || _pinSet.Item2;
|
||||||
|
FingerprintLock = await _lockService.IsFingerprintLockSetAsync();
|
||||||
_email = await _userService.GetEmailAsync();
|
_email = await _userService.GetEmailAsync();
|
||||||
PageTitle = PinLock ? AppResources.VerifyPIN : AppResources.VerifyMasterPassword;
|
PageTitle = PinLock ? AppResources.VerifyPIN : AppResources.VerifyMasterPassword;
|
||||||
|
|
||||||
|
if(FingerprintLock)
|
||||||
|
{
|
||||||
|
var tasks = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
await Task.Delay(500);
|
||||||
|
Device.BeginInvokeOnMainThread(async () => {
|
||||||
|
var success = await _platformUtilsService.AuthenticateFingerprintAsync();
|
||||||
|
_lockService.FingerprintLocked = !success;
|
||||||
|
if(success)
|
||||||
|
{
|
||||||
|
DoContinue();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task SubmitAsync()
|
public async Task SubmitAsync()
|
||||||
|
@ -102,6 +102,11 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
await _vm.UpdatePinAsync();
|
await _vm.UpdatePinAsync();
|
||||||
}
|
}
|
||||||
|
else if(item.Name.Contains(AppResources.Fingerprint) || item.Name.Contains(AppResources.TouchID) ||
|
||||||
|
item.Name.Contains(AppResources.FaceID))
|
||||||
|
{
|
||||||
|
await _vm.UpdateFingerprintAsync();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -80,7 +80,7 @@ namespace Bit.App.Pages
|
|||||||
_lockOptionValue = _lockOptions.FirstOrDefault(o => o.Value == option).Key;
|
_lockOptionValue = _lockOptions.FirstOrDefault(o => o.Value == option).Key;
|
||||||
var pinSet = await _lockService.IsPinLockSetAsync();
|
var pinSet = await _lockService.IsPinLockSetAsync();
|
||||||
_pin = pinSet.Item1 || pinSet.Item2;
|
_pin = pinSet.Item1 || pinSet.Item2;
|
||||||
// TODO: Fingerprint
|
_fingerprint = await _lockService.IsFingerprintLockSetAsync();
|
||||||
BuildList();
|
BuildList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -238,7 +238,21 @@ namespace Bit.App.Pages
|
|||||||
await _storageService.RemoveAsync(Constants.PinProtectedKey);
|
await _storageService.RemoveAsync(Constants.PinProtectedKey);
|
||||||
await _storageService.RemoveAsync(Constants.ProtectedPin);
|
await _storageService.RemoveAsync(Constants.ProtectedPin);
|
||||||
}
|
}
|
||||||
|
BuildList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task UpdateFingerprintAsync()
|
||||||
|
{
|
||||||
|
_fingerprint = !_fingerprint;
|
||||||
|
if(_fingerprint)
|
||||||
|
{
|
||||||
|
await _storageService.SaveAsync(Constants.FingerprintUnlockKey, true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await _storageService.RemoveAsync(Constants.FingerprintUnlockKey);
|
||||||
|
}
|
||||||
|
await _cryptoService.ToggleKeyAsync();
|
||||||
BuildList();
|
BuildList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -200,29 +200,25 @@ namespace Bit.App.Services
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> AuthenticateFingerprintAsync(string text = null, Action fallback = null)
|
public async Task<bool> AuthenticateFingerprintAsync(string text = null)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if(text == null)
|
if(text == null)
|
||||||
{
|
{
|
||||||
text = AppResources.FingerprintDirection;
|
text = AppResources.FingerprintDirection;
|
||||||
|
// TODO: face id direction
|
||||||
}
|
}
|
||||||
var fingerprintRequest = new AuthenticationRequestConfiguration(text)
|
var fingerprintRequest = new AuthenticationRequestConfiguration(text)
|
||||||
{
|
{
|
||||||
AllowAlternativeAuthentication = true,
|
AllowAlternativeAuthentication = true,
|
||||||
CancelTitle = AppResources.Cancel,
|
CancelTitle = AppResources.Cancel
|
||||||
FallbackTitle = AppResources.LogOut
|
|
||||||
};
|
};
|
||||||
var result = await CrossFingerprint.Current.AuthenticateAsync(fingerprintRequest);
|
var result = await CrossFingerprint.Current.AuthenticateAsync(fingerprintRequest);
|
||||||
if(result.Authenticated)
|
if(result.Authenticated)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else if(result.Status == FingerprintAuthenticationResultStatus.FallbackRequested)
|
|
||||||
{
|
|
||||||
fallback?.Invoke();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch { }
|
catch { }
|
||||||
return false;
|
return false;
|
||||||
|
@ -6,11 +6,13 @@ namespace Bit.Core.Abstractions
|
|||||||
public interface ILockService
|
public interface ILockService
|
||||||
{
|
{
|
||||||
bool PinLocked { get; set; }
|
bool PinLocked { get; set; }
|
||||||
|
bool FingerprintLocked { get; set; }
|
||||||
|
|
||||||
Task CheckLockAsync();
|
Task CheckLockAsync();
|
||||||
Task ClearAsync();
|
Task ClearAsync();
|
||||||
Task<bool> IsLockedAsync();
|
Task<bool> IsLockedAsync();
|
||||||
Task<Tuple<bool, bool>> IsPinLockSetAsync();
|
Task<Tuple<bool, bool>> IsPinLockSetAsync();
|
||||||
|
Task<bool> IsFingerprintLockSetAsync();
|
||||||
Task LockAsync(bool allowSoftLock = false);
|
Task LockAsync(bool allowSoftLock = false);
|
||||||
Task SetLockOptionAsync(int? lockOption);
|
Task SetLockOptionAsync(int? lockOption);
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,6 @@ namespace Bit.Core.Abstractions
|
|||||||
bool SupportsU2f();
|
bool SupportsU2f();
|
||||||
bool SupportsDuo();
|
bool SupportsDuo();
|
||||||
Task<bool> SupportsFingerprintAsync();
|
Task<bool> SupportsFingerprintAsync();
|
||||||
Task<bool> AuthenticateFingerprintAsync(string text = null, Action fallback = null);
|
Task<bool> AuthenticateFingerprintAsync(string text = null);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -6,6 +6,7 @@
|
|||||||
public const string iOSAppProtocol = "iosapp://";
|
public const string iOSAppProtocol = "iosapp://";
|
||||||
public static string LockOptionKey = "lockOption";
|
public static string LockOptionKey = "lockOption";
|
||||||
public static string LastActiveKey = "lastActive";
|
public static string LastActiveKey = "lastActive";
|
||||||
|
public static string FingerprintUnlockKey = "fingerprintUnlock";
|
||||||
public static string ProtectedPin = "protectedPin";
|
public static string ProtectedPin = "protectedPin";
|
||||||
public static string PinProtectedKey = "pinProtectedKey";
|
public static string PinProtectedKey = "pinProtectedKey";
|
||||||
public static string DefaultUriMatch = "defaultUriMatch";
|
public static string DefaultUriMatch = "defaultUriMatch";
|
||||||
|
@ -47,7 +47,8 @@ namespace Bit.Core.Services
|
|||||||
{
|
{
|
||||||
_key = key;
|
_key = key;
|
||||||
var option = await _storageService.GetAsync<int?>(Constants.LockOptionKey);
|
var option = await _storageService.GetAsync<int?>(Constants.LockOptionKey);
|
||||||
if(option.HasValue)
|
var fingerprint = await _storageService.GetAsync<bool?>(Constants.FingerprintUnlockKey);
|
||||||
|
if(option.HasValue && !fingerprint.GetValueOrDefault())
|
||||||
{
|
{
|
||||||
// If we have a lock option set, we do not store the key
|
// If we have a lock option set, we do not store the key
|
||||||
return;
|
return;
|
||||||
@ -349,7 +350,8 @@ namespace Bit.Core.Services
|
|||||||
{
|
{
|
||||||
var key = await GetKeyAsync();
|
var key = await GetKeyAsync();
|
||||||
var option = await _storageService.GetAsync<int?>(Constants.LockOptionKey);
|
var option = await _storageService.GetAsync<int?>(Constants.LockOptionKey);
|
||||||
if(option != null || option == 0)
|
var fingerprint = await _storageService.GetAsync<bool?>(Constants.FingerprintUnlockKey);
|
||||||
|
if(!fingerprint.GetValueOrDefault() && (option != null || option == 0))
|
||||||
{
|
{
|
||||||
await ClearKeyAsync();
|
await ClearKeyAsync();
|
||||||
_key = key;
|
_key = key;
|
||||||
|
@ -39,16 +39,28 @@ namespace Bit.Core.Services
|
|||||||
}
|
}
|
||||||
|
|
||||||
public bool PinLocked { get; set; }
|
public bool PinLocked { get; set; }
|
||||||
|
public bool FingerprintLocked { get; set; } = true;
|
||||||
|
|
||||||
// TODO: init timer?
|
// TODO: init timer?
|
||||||
|
|
||||||
public async Task<bool> IsLockedAsync()
|
public async Task<bool> IsLockedAsync()
|
||||||
{
|
{
|
||||||
var hasKey = await _cryptoService.HasKeyAsync();
|
var hasKey = await _cryptoService.HasKeyAsync();
|
||||||
if(hasKey && PinLocked)
|
if(hasKey)
|
||||||
|
{
|
||||||
|
if(PinLocked)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var fingerprintSet = await IsFingerprintLockSetAsync();
|
||||||
|
if(fingerprintSet && FingerprintLocked)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return !hasKey;
|
return !hasKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,6 +114,13 @@ namespace Bit.Core.Services
|
|||||||
if(pinSet.Item1)
|
if(pinSet.Item1)
|
||||||
{
|
{
|
||||||
PinLocked = true;
|
PinLocked = true;
|
||||||
|
}
|
||||||
|
if(await IsFingerprintLockSetAsync())
|
||||||
|
{
|
||||||
|
FingerprintLocked = true;
|
||||||
|
}
|
||||||
|
if(FingerprintLocked || PinLocked)
|
||||||
|
{
|
||||||
_messagingService.Send("locked");
|
_messagingService.Send("locked");
|
||||||
// TODO: locked callback?
|
// TODO: locked callback?
|
||||||
return;
|
return;
|
||||||
@ -134,6 +153,12 @@ namespace Bit.Core.Services
|
|||||||
return new Tuple<bool, bool>(protectedPin != null, pinProtectedKey != null);
|
return new Tuple<bool, bool>(protectedPin != null, pinProtectedKey != null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<bool> IsFingerprintLockSetAsync()
|
||||||
|
{
|
||||||
|
var fingerprintLock = await _storageService.GetAsync<bool?>(Constants.FingerprintUnlockKey);
|
||||||
|
return fingerprintLock.GetValueOrDefault();
|
||||||
|
}
|
||||||
|
|
||||||
public async Task ClearAsync()
|
public async Task ClearAsync()
|
||||||
{
|
{
|
||||||
await _storageService.RemoveAsync(Constants.ProtectedPin);
|
await _storageService.RemoveAsync(Constants.ProtectedPin);
|
||||||
|
Loading…
Reference in New Issue
Block a user