diff --git a/src/Android/MainActivity.cs b/src/Android/MainActivity.cs index 30a4acd1e..8d0cf4885 100644 --- a/src/Android/MainActivity.cs +++ b/src/Android/MainActivity.cs @@ -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); + } } } diff --git a/src/App/Abstractions/Services/IAppSettingsService.cs b/src/App/Abstractions/Services/IAppSettingsService.cs index f10fe8a5f..1cd80ff54 100644 --- a/src/App/Abstractions/Services/IAppSettingsService.cs +++ b/src/App/Abstractions/Services/IAppSettingsService.cs @@ -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; } diff --git a/src/App/Constants.cs b/src/App/Constants.cs index 1555284a8..b5751c5d7 100644 --- a/src/App/Constants.cs +++ b/src/App/Constants.cs @@ -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; diff --git a/src/App/Controls/ExtendedContentPage.cs b/src/App/Controls/ExtendedContentPage.cs index 6df9eead5..8c591d921 100644 --- a/src/App/Controls/ExtendedContentPage.cs +++ b/src/App/Controls/ExtendedContentPage.cs @@ -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(); _googleAnalyticsService = Resolver.Resolve(); _lockService = Resolver.Resolve(); _deviceActionService = Resolver.Resolve(); + _authService = Resolver.Resolve(); 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.Current, "SyncCompleted", diff --git a/src/App/Pages/EnvironmentPage.cs b/src/App/Pages/EnvironmentPage.cs index fe1ecae4d..85304a0be 100644 --- a/src/App/Pages/EnvironmentPage.cs +++ b/src/App/Pages/EnvironmentPage.cs @@ -16,7 +16,7 @@ namespace Bit.App.Pages private IGoogleAnalyticsService _googleAnalyticsService; public EnvironmentPage() - : base(updateActivity: false) + : base(updateActivity: false, requireAuth: false) { _appSettings = Resolver.Resolve(); _deviceActionService = Resolver.Resolve(); diff --git a/src/App/Pages/HomePage.cs b/src/App/Pages/HomePage.cs index d39f0925c..6e95f6dfb 100644 --- a/src/App/Pages/HomePage.cs +++ b/src/App/Pages/HomePage.cs @@ -18,7 +18,7 @@ namespace Bit.App.Pages private DateTime? _lastAction; public HomePage() - : base(updateActivity: false) + : base(updateActivity: false, requireAuth: false) { _authService = Resolver.Resolve(); _deviceActionService = Resolver.Resolve(); diff --git a/src/App/Pages/Lock/LockPinPage.cs b/src/App/Pages/Lock/LockPinPage.cs index f371cdcb6..75aab6d08 100644 --- a/src/App/Pages/Lock/LockPinPage.cs +++ b/src/App/Pages/Lock/LockPinPage.cs @@ -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; diff --git a/src/App/Pages/LoginPage.cs b/src/App/Pages/LoginPage.cs index 1c527a459..5d8a2bc47 100644 --- a/src/App/Pages/LoginPage.cs +++ b/src/App/Pages/LoginPage.cs @@ -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(); diff --git a/src/App/Pages/LoginTwoFactorPage.cs b/src/App/Pages/LoginTwoFactorPage.cs index cdaaa873b..93a26e689 100644 --- a/src/App/Pages/LoginTwoFactorPage.cs +++ b/src/App/Pages/LoginTwoFactorPage.cs @@ -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(); diff --git a/src/App/Pages/PasswordHintPage.cs b/src/App/Pages/PasswordHintPage.cs index cae77ef51..c38ddb798 100644 --- a/src/App/Pages/PasswordHintPage.cs +++ b/src/App/Pages/PasswordHintPage.cs @@ -17,7 +17,7 @@ namespace Bit.App.Pages private IDeviceActionService _deviceActionService; public PasswordHintPage() - : base(updateActivity: false) + : base(updateActivity: false, requireAuth: false) { _accountApiRepository = Resolver.Resolve(); _deviceActionService = Resolver.Resolve(); diff --git a/src/App/Pages/RegisterPage.cs b/src/App/Pages/RegisterPage.cs index 3e0fc5d52..991c1ab84 100644 --- a/src/App/Pages/RegisterPage.cs +++ b/src/App/Pages/RegisterPage.cs @@ -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(); diff --git a/src/App/Pages/ScanPage.cs b/src/App/Pages/ScanPage.cs index 2e8b8c784..fbb190550 100644 --- a/src/App/Pages/ScanPage.cs +++ b/src/App/Pages/ScanPage.cs @@ -14,7 +14,7 @@ namespace Bit.App.Pages private bool _pageDisappeared = true; public ScanPage(Action callback) - : base(updateActivity: false) + : base(updateActivity: false, requireAuth: false) { _zxing = new ZXingScannerView { diff --git a/src/App/Services/AppSettingsService.cs b/src/App/Services/AppSettingsService.cs index be2bc2f01..68a9d18a6 100644 --- a/src/App/Services/AppSettingsService.cs +++ b/src/App/Services/AppSettingsService.cs @@ -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 diff --git a/src/App/Services/AuthService.cs b/src/App/Services/AuthService.cs index f0f547783..da93e9b4e 100644 --- a/src/App/Services/AuthService.cs +++ b/src/App/Services/AuthService.cs @@ -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 TokenPostTwoFactorAsync(TwoFactorProviderType type, string token, bool remember, + public async Task 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) {