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()) 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 public interface IAppSettingsService
{ {
bool Locked { get; set; } bool Locked { get; set; }
int FailedPinAttempts { get; set; }
DateTime LastActivity { get; set; } DateTime LastActivity { get; set; }
DateTime LastCacheClear { get; set; } DateTime LastCacheClear { get; set; }
bool AutofillPersistNotification { get; set; } bool AutofillPersistNotification { get; set; }

View file

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

View file

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

View file

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

View file

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

View file

@ -124,12 +124,19 @@ namespace Bit.App.Pages
if(Model.PIN == _authService.PIN) if(Model.PIN == _authService.PIN)
{ {
_appSettingsService.Locked = false; _appSettingsService.Locked = false;
_appSettingsService.FailedPinAttempts = 0;
PinControl.Entry.Unfocus(); PinControl.Entry.Unfocus();
await Navigation.PopModalAsync(); await Navigation.PopModalAsync();
} }
else 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); await DisplayAlert(null, AppResources.InvalidPIN, AppResources.Ok);
Model.PIN = string.Empty; Model.PIN = string.Empty;

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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