using System; using System.Threading.Tasks; using Bit.App.Abstractions; using Bit.App.Controls; using Bit.App.Resources; using Bit.App.Utilities; using Bit.Core; using Bit.Core.Abstractions; using Bit.Core.Exceptions; using Bit.Core.Utilities; using Xamarin.Forms; namespace Bit.App.Pages { public class LoginPageViewModel : CaptchaProtectedViewModel { private readonly IDeviceActionService _deviceActionService; private readonly IAuthService _authService; private readonly ISyncService _syncService; private readonly IPlatformUtilsService _platformUtilsService; private readonly IStateService _stateService; private readonly IEnvironmentService _environmentService; private readonly II18nService _i18nService; private readonly IMessagingService _messagingService; private readonly ILogger _logger; private bool _showPassword; private bool _showCancelButton; private string _email; private string _masterPassword; public LoginPageViewModel() { _deviceActionService = ServiceContainer.Resolve("deviceActionService"); _authService = ServiceContainer.Resolve("authService"); _syncService = ServiceContainer.Resolve("syncService"); _platformUtilsService = ServiceContainer.Resolve("platformUtilsService"); _stateService = ServiceContainer.Resolve("stateService"); _environmentService = ServiceContainer.Resolve("environmentService"); _i18nService = ServiceContainer.Resolve("i18nService"); _messagingService = ServiceContainer.Resolve("messagingService"); _logger = ServiceContainer.Resolve("logger"); PageTitle = AppResources.Bitwarden; TogglePasswordCommand = new Command(TogglePassword); LogInCommand = new Command(async () => await LogInAsync()); AccountSwitchingOverlayViewModel = new AccountSwitchingOverlayViewModel(_stateService, _messagingService, _logger) { AllowAddAccountRow = true, AllowActiveAccountSelection = true }; } public bool ShowPassword { get => _showPassword; set => SetProperty(ref _showPassword, value, additionalPropertyNames: new string[] { nameof(ShowPasswordIcon) }); } public bool ShowCancelButton { get => _showCancelButton; set => SetProperty(ref _showCancelButton, value); } public string Email { get => _email; set => SetProperty(ref _email, value); } public string MasterPassword { get => _masterPassword; set => SetProperty(ref _masterPassword, value); } public AccountSwitchingOverlayViewModel AccountSwitchingOverlayViewModel { get; } public Command LogInCommand { get; } public Command TogglePasswordCommand { get; } public string ShowPasswordIcon => ShowPassword ? BitwardenIcons.EyeSlash : BitwardenIcons.Eye; public Action StartTwoFactorAction { get; set; } public Action LogInSuccessAction { get; set; } public Action UpdateTempPasswordAction { get; set; } public Action CloseAction { get; set; } protected override II18nService i18nService => _i18nService; protected override IEnvironmentService environmentService => _environmentService; protected override IDeviceActionService deviceActionService => _deviceActionService; protected override IPlatformUtilsService platformUtilsService => _platformUtilsService; public async Task InitAsync() { if (string.IsNullOrWhiteSpace(Email)) { Email = await _stateService.GetRememberedEmailAsync(); } } public async Task LogInAsync(bool showLoading = true, bool checkForExistingAccount = false) { if (Xamarin.Essentials.Connectivity.NetworkAccess == Xamarin.Essentials.NetworkAccess.None) { await _platformUtilsService.ShowDialogAsync(AppResources.InternetConnectionRequiredMessage, AppResources.InternetConnectionRequiredTitle, AppResources.Ok); return; } if (string.IsNullOrWhiteSpace(Email)) { await _platformUtilsService.ShowDialogAsync( string.Format(AppResources.ValidationFieldRequired, AppResources.EmailAddress), AppResources.AnErrorHasOccurred, AppResources.Ok); return; } if (!Email.Contains("@")) { await _platformUtilsService.ShowDialogAsync(AppResources.InvalidEmail, AppResources.AnErrorHasOccurred, AppResources.Ok); return; } if (string.IsNullOrWhiteSpace(MasterPassword)) { await _platformUtilsService.ShowDialogAsync( string.Format(AppResources.ValidationFieldRequired, AppResources.MasterPassword), AppResources.AnErrorHasOccurred, AppResources.Ok); return; } ShowPassword = false; try { if (checkForExistingAccount) { var userId = await _stateService.GetUserIdAsync(Email); if (!string.IsNullOrWhiteSpace(userId)) { var userEnvUrls = await _stateService.GetEnvironmentUrlsAsync(userId); if (userEnvUrls?.Base == _environmentService.BaseUrl) { await PromptToSwitchToExistingAccountAsync(userId); return; } } } if (showLoading) { await _deviceActionService.ShowLoadingAsync(AppResources.LoggingIn); } var response = await _authService.LogInAsync(Email, MasterPassword, _captchaToken); await _stateService.SetRememberedEmailAsync(Email); await AppHelpers.ResetInvalidUnlockAttemptsAsync(); if (response.CaptchaNeeded) { if (await HandleCaptchaAsync(response.CaptchaSiteKey)) { await LogInAsync(false); _captchaToken = null; } return; } MasterPassword = string.Empty; _captchaToken = null; await _deviceActionService.HideLoadingAsync(); if (response.TwoFactor) { StartTwoFactorAction?.Invoke(); } else if (response.ForcePasswordReset) { UpdateTempPasswordAction?.Invoke(); } else { var task = Task.Run(async () => await _syncService.FullSyncAsync(true)); LogInSuccessAction?.Invoke(); } } catch (ApiException e) { _captchaToken = null; MasterPassword = string.Empty; await _deviceActionService.HideLoadingAsync(); if (e?.Error != null) { await _platformUtilsService.ShowDialogAsync(e.Error.GetSingleMessage(), AppResources.AnErrorHasOccurred, AppResources.Ok); } } } public void TogglePassword() { ShowPassword = !ShowPassword; var entry = (Page as LoginPage).MasterPasswordEntry; entry.Focus(); entry.CursorPosition = String.IsNullOrEmpty(MasterPassword) ? 0 : MasterPassword.Length; } public async Task RemoveAccountAsync() { try { var confirmed = await _platformUtilsService.ShowDialogAsync(AppResources.RemoveAccountConfirmation, AppResources.RemoveAccount, AppResources.Yes, AppResources.Cancel); if (confirmed) { _messagingService.Send("logout"); } } catch (Exception e) { _logger.Exception(e); } } private async Task PromptToSwitchToExistingAccountAsync(string userId) { var switchToAccount = await _platformUtilsService.ShowDialogAsync( AppResources.SwitchToAlreadyAddedAccountConfirmation, AppResources.AccountAlreadyAdded, AppResources.Yes, AppResources.Cancel); if (switchToAccount) { await _stateService.SetActiveUserAsync(userId); _messagingService.Send("switchedAccount"); } } } }