log out after 5 failed pin attempts

This commit is contained in:
Kyle Spearrin 2018-01-18 13:18:08 -05:00
parent 1390df48b6
commit 53f406a267
14 changed files with 55 additions and 12 deletions

View file

@ -139,7 +139,14 @@ namespace Bit.Android
if(Utilities.NfcEnabled())
{
MessagingCenter.Send(Xamarin.Forms.Application.Current, "ResumeYubiKey");
try
{
MessagingCenter.Send(Xamarin.Forms.Application.Current, "ResumeYubiKey");
}
catch(Exception e)
{
System.Diagnostics.Debug.WriteLine(e);
}
}
}

View file

@ -5,6 +5,7 @@ namespace Bit.App.Abstractions
public interface IAppSettingsService
{
bool Locked { get; set; }
int FailedPinAttempts { get; set; }
DateTime LastActivity { get; set; }
DateTime LastCacheClear { get; set; }
bool AutofillPersistNotification { get; set; }

View file

@ -45,6 +45,7 @@
public const string ApiUrl = "other:apiUrl";
public const string IdentityUrl = "other:identityUrl";
public const string IconsUrl = "other:iconsUrl";
public const string FailedPinAttempts = "other:failedPinAttempts";
public const int SelectFileRequestCode = 42;
public const int SelectFilePermissionRequestCode = 43;

View file

@ -1,4 +1,5 @@
using Bit.App.Abstractions;
using Bit.App.Pages;
using Xamarin.Forms;
using XLabs.Ioc;
@ -10,23 +11,33 @@ namespace Bit.App.Controls
private IGoogleAnalyticsService _googleAnalyticsService;
private ILockService _lockService;
private IDeviceActionService _deviceActionService;
private IAuthService _authService;
private bool _syncIndicator;
private bool _updateActivity;
private bool _requireAuth;
public ExtendedContentPage(bool syncIndicator = false, bool updateActivity = true)
public ExtendedContentPage(bool syncIndicator = false, bool updateActivity = true, bool requireAuth = true)
{
_syncIndicator = syncIndicator;
_updateActivity = updateActivity;
_requireAuth = requireAuth;
_syncService = Resolver.Resolve<ISyncService>();
_googleAnalyticsService = Resolver.Resolve<IGoogleAnalyticsService>();
_lockService = Resolver.Resolve<ILockService>();
_deviceActionService = Resolver.Resolve<IDeviceActionService>();
_authService = Resolver.Resolve<IAuthService>();
BackgroundColor = Color.FromHex("efeff4");
}
protected override void OnAppearing()
{
if(_requireAuth && !_authService.IsAuthenticated)
{
Device.BeginInvokeOnMainThread(
() => Application.Current.MainPage = new ExtendedNavigationPage(new HomePage()));
}
if(_syncIndicator)
{
MessagingCenter.Subscribe<Application, bool>(Application.Current, "SyncCompleted",

View file

@ -16,7 +16,7 @@ namespace Bit.App.Pages
private IGoogleAnalyticsService _googleAnalyticsService;
public EnvironmentPage()
: base(updateActivity: false)
: base(updateActivity: false, requireAuth: false)
{
_appSettings = Resolver.Resolve<IAppSettingsService>();
_deviceActionService = Resolver.Resolve<IDeviceActionService>();

View file

@ -18,7 +18,7 @@ namespace Bit.App.Pages
private DateTime? _lastAction;
public HomePage()
: base(updateActivity: false)
: base(updateActivity: false, requireAuth: false)
{
_authService = Resolver.Resolve<IAuthService>();
_deviceActionService = Resolver.Resolve<IDeviceActionService>();

View file

@ -124,12 +124,19 @@ namespace Bit.App.Pages
if(Model.PIN == _authService.PIN)
{
_appSettingsService.Locked = false;
_appSettingsService.FailedPinAttempts = 0;
PinControl.Entry.Unfocus();
await Navigation.PopModalAsync();
}
else
{
// TODO: keep track of invalid attempts and logout?
_appSettingsService.FailedPinAttempts++;
if(_appSettingsService.FailedPinAttempts >= 5)
{
PinControl.Entry.Unfocus();
AuthService.LogOut();
return;
}
await DisplayAlert(null, AppResources.InvalidPIN, AppResources.Ok);
Model.PIN = string.Empty;

View file

@ -21,7 +21,7 @@ namespace Bit.App.Pages
private readonly string _email;
public LoginPage(string email = null)
: base(updateActivity: false)
: base(updateActivity: false, requireAuth: false)
{
_email = email;
_authService = Resolver.Resolve<IAuthService>();

View file

@ -33,7 +33,7 @@ namespace Bit.App.Pages
private readonly FullLoginResult _result;
public LoginTwoFactorPage(string email, FullLoginResult result, TwoFactorProviderType? type = null)
: base(updateActivity: false)
: base(updateActivity: false, requireAuth: false)
{
_deviceInfoService = Resolver.Resolve<IDeviceInfoService>();

View file

@ -17,7 +17,7 @@ namespace Bit.App.Pages
private IDeviceActionService _deviceActionService;
public PasswordHintPage()
: base(updateActivity: false)
: base(updateActivity: false, requireAuth: false)
{
_accountApiRepository = Resolver.Resolve<IAccountsApiRepository>();
_deviceActionService = Resolver.Resolve<IDeviceActionService>();

View file

@ -20,7 +20,7 @@ namespace Bit.App.Pages
private HomePage _homePage;
public RegisterPage(HomePage homePage)
: base(updateActivity: false)
: base(updateActivity: false, requireAuth: false)
{
_homePage = homePage;
_cryptoService = Resolver.Resolve<ICryptoService>();

View file

@ -14,7 +14,7 @@ namespace Bit.App.Pages
private bool _pageDisappeared = true;
public ScanPage(Action<string> callback)
: base(updateActivity: false)
: base(updateActivity: false, requireAuth: false)
{
_zxing = new ZXingScannerView
{

View file

@ -26,6 +26,18 @@ namespace Bit.App.Services
}
}
public int FailedPinAttempts
{
get
{
return _settings.GetValueOrDefault(Constants.FailedPinAttempts, 0);
}
set
{
_settings.AddOrUpdateValue(Constants.FailedPinAttempts, value);
}
}
public DateTime LastActivity
{
get

View file

@ -24,6 +24,7 @@ namespace Bit.App.Services
private readonly ISecureStorageService _secureStorage;
private readonly ITokenService _tokenService;
private readonly ISettings _settings;
private readonly IAppSettingsService _appSettingsService;
private readonly ICryptoService _cryptoService;
private readonly IConnectApiRepository _connectApiRepository;
private readonly IAccountsApiRepository _accountsApiRepository;
@ -41,6 +42,7 @@ namespace Bit.App.Services
ISecureStorageService secureStorage,
ITokenService tokenService,
ISettings settings,
IAppSettingsService appSettingsService,
ICryptoService cryptoService,
IConnectApiRepository connectApiRepository,
IAccountsApiRepository accountsApiRepository,
@ -52,6 +54,7 @@ namespace Bit.App.Services
_secureStorage = secureStorage;
_tokenService = tokenService;
_settings = settings;
_appSettingsService = appSettingsService;
_cryptoService = cryptoService;
_connectApiRepository = connectApiRepository;
_accountsApiRepository = accountsApiRepository;
@ -269,10 +272,10 @@ namespace Bit.App.Services
return result;
}
public async Task<Models.LoginResult> TokenPostTwoFactorAsync(TwoFactorProviderType type, string token, bool remember,
public async Task<LoginResult> TokenPostTwoFactorAsync(TwoFactorProviderType type, string token, bool remember,
string email, string masterPasswordHash, SymmetricCryptoKey key)
{
var result = new Models.LoginResult();
var result = new LoginResult();
var request = new TokenRequest
{
@ -315,6 +318,7 @@ namespace Bit.App.Services
UserId = _tokenService.TokenUserId;
Email = _tokenService.TokenEmail;
_settings.AddOrUpdateValue(Constants.LastLoginEmail, Email);
_appSettingsService.FailedPinAttempts = 0;
if(response.PrivateKey != null)
{