2016-07-23 09:17:11 +03:00
|
|
|
|
using System;
|
|
|
|
|
using Bit.App.Abstractions;
|
|
|
|
|
using Bit.App.Controls;
|
|
|
|
|
using Bit.App.Resources;
|
|
|
|
|
using Xamarin.Forms;
|
|
|
|
|
using XLabs.Ioc;
|
|
|
|
|
using System.Threading.Tasks;
|
2017-04-20 06:16:09 +03:00
|
|
|
|
using Bit.App.Models;
|
2017-05-30 21:13:53 +03:00
|
|
|
|
using Bit.App.Utilities;
|
2017-06-27 23:18:32 +03:00
|
|
|
|
using Bit.App.Enums;
|
|
|
|
|
using System.Collections.Generic;
|
2017-06-28 17:09:52 +03:00
|
|
|
|
using System.Net;
|
2017-06-29 05:24:04 +03:00
|
|
|
|
using FFImageLoading.Forms;
|
2016-07-23 09:17:11 +03:00
|
|
|
|
|
|
|
|
|
namespace Bit.App.Pages
|
|
|
|
|
{
|
|
|
|
|
public class LoginTwoFactorPage : ExtendedContentPage
|
|
|
|
|
{
|
2017-06-29 05:24:04 +03:00
|
|
|
|
private DateTime? _lastAction;
|
2016-07-23 09:17:11 +03:00
|
|
|
|
private IAuthService _authService;
|
|
|
|
|
private ISyncService _syncService;
|
2017-06-29 05:24:04 +03:00
|
|
|
|
private IDeviceInfoService _deviceInfoService;
|
2017-11-22 07:08:45 +03:00
|
|
|
|
private IDeviceActionService _deviceActionService;
|
2017-02-06 17:39:07 +03:00
|
|
|
|
private IGoogleAnalyticsService _googleAnalyticsService;
|
2017-06-29 18:22:06 +03:00
|
|
|
|
private ITwoFactorApiRepository _twoFactorApiRepository;
|
2017-10-10 15:25:23 +03:00
|
|
|
|
private IPushNotificationService _pushNotification;
|
2017-08-29 00:50:17 +03:00
|
|
|
|
private IAppSettingsService _appSettingsService;
|
2017-02-06 17:39:07 +03:00
|
|
|
|
private readonly string _email;
|
|
|
|
|
private readonly string _masterPasswordHash;
|
2017-04-22 21:36:31 +03:00
|
|
|
|
private readonly SymmetricCryptoKey _key;
|
2017-06-27 23:18:32 +03:00
|
|
|
|
private readonly Dictionary<TwoFactorProviderType, Dictionary<string, object>> _providers;
|
2017-06-29 18:22:06 +03:00
|
|
|
|
private TwoFactorProviderType? _providerType;
|
2017-06-28 00:10:40 +03:00
|
|
|
|
private readonly FullLoginResult _result;
|
2018-04-03 22:59:58 +03:00
|
|
|
|
private readonly string _duoOrgTitle;
|
2017-02-06 17:39:07 +03:00
|
|
|
|
|
2017-06-27 23:18:32 +03:00
|
|
|
|
public LoginTwoFactorPage(string email, FullLoginResult result, TwoFactorProviderType? type = null)
|
2018-01-18 21:18:08 +03:00
|
|
|
|
: base(updateActivity: false, requireAuth: false)
|
2016-07-23 09:17:11 +03:00
|
|
|
|
{
|
2018-04-03 22:59:58 +03:00
|
|
|
|
_duoOrgTitle = $"Duo ({AppResources.Organization})";
|
2017-06-29 05:24:04 +03:00
|
|
|
|
_deviceInfoService = Resolver.Resolve<IDeviceInfoService>();
|
|
|
|
|
|
2017-02-06 17:39:07 +03:00
|
|
|
|
_email = email;
|
2017-06-28 00:10:40 +03:00
|
|
|
|
_result = result;
|
2017-06-27 23:18:32 +03:00
|
|
|
|
_masterPasswordHash = result.MasterPasswordHash;
|
|
|
|
|
_key = result.Key;
|
|
|
|
|
_providers = result.TwoFactorProviders;
|
|
|
|
|
_providerType = type ?? GetDefaultProvider();
|
2017-02-06 17:39:07 +03:00
|
|
|
|
|
2017-11-22 07:08:45 +03:00
|
|
|
|
_deviceActionService = Resolver.Resolve<IDeviceActionService>();
|
2016-07-23 09:17:11 +03:00
|
|
|
|
_authService = Resolver.Resolve<IAuthService>();
|
|
|
|
|
_syncService = Resolver.Resolve<ISyncService>();
|
2017-08-29 00:50:17 +03:00
|
|
|
|
_appSettingsService = Resolver.Resolve<IAppSettingsService>();
|
2017-02-06 17:39:07 +03:00
|
|
|
|
_googleAnalyticsService = Resolver.Resolve<IGoogleAnalyticsService>();
|
2017-06-29 18:22:06 +03:00
|
|
|
|
_twoFactorApiRepository = Resolver.Resolve<ITwoFactorApiRepository>();
|
2017-10-10 15:25:23 +03:00
|
|
|
|
_pushNotification = Resolver.Resolve<IPushNotificationService>();
|
2016-07-23 09:17:11 +03:00
|
|
|
|
|
|
|
|
|
Init();
|
|
|
|
|
}
|
|
|
|
|
|
2017-06-27 23:18:32 +03:00
|
|
|
|
public FormEntryCell TokenCell { get; set; }
|
|
|
|
|
public ExtendedSwitchCell RememberCell { get; set; }
|
2016-07-23 09:17:11 +03:00
|
|
|
|
|
|
|
|
|
private void Init()
|
|
|
|
|
{
|
2017-06-29 18:22:06 +03:00
|
|
|
|
SubscribeYubiKey(true);
|
|
|
|
|
if(_providers.Count > 1)
|
|
|
|
|
{
|
|
|
|
|
var sendEmailTask = SendEmailAsync(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ToolbarItems.Clear();
|
2017-06-27 23:18:32 +03:00
|
|
|
|
var scrollView = new ScrollView();
|
2016-08-14 04:43:15 +03:00
|
|
|
|
|
2017-06-29 05:24:04 +03:00
|
|
|
|
var anotherMethodButton = new ExtendedButton
|
|
|
|
|
{
|
2017-06-29 19:11:07 +03:00
|
|
|
|
Text = AppResources.UseAnotherTwoStepMethod,
|
2017-06-29 05:24:04 +03:00
|
|
|
|
Style = (Style)Application.Current.Resources["btn-primaryAccent"],
|
|
|
|
|
Margin = new Thickness(15, 0, 15, 25),
|
|
|
|
|
Command = new Command(() => AnotherMethodAsync()),
|
|
|
|
|
Uppercase = false,
|
2017-06-29 18:22:06 +03:00
|
|
|
|
BackgroundColor = Color.Transparent,
|
|
|
|
|
VerticalOptions = LayoutOptions.Start
|
2017-06-29 05:24:04 +03:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
var instruction = new Label
|
|
|
|
|
{
|
|
|
|
|
LineBreakMode = LineBreakMode.WordWrap,
|
|
|
|
|
Margin = new Thickness(15),
|
|
|
|
|
HorizontalTextAlignment = TextAlignment.Center
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
RememberCell = new ExtendedSwitchCell
|
|
|
|
|
{
|
2017-06-29 19:11:07 +03:00
|
|
|
|
Text = AppResources.RememberMe,
|
2017-06-29 05:24:04 +03:00
|
|
|
|
On = false
|
|
|
|
|
};
|
|
|
|
|
|
2018-01-18 16:09:18 +03:00
|
|
|
|
var continueToolbarItem = new ToolbarItem(AppResources.Continue,
|
|
|
|
|
Helpers.ToolbarImage("ion_chevron_right.png"), async () =>
|
2018-01-18 15:55:06 +03:00
|
|
|
|
{
|
|
|
|
|
var token = TokenCell?.Entry.Text.Trim().Replace(" ", "");
|
|
|
|
|
await LogInAsync(token);
|
|
|
|
|
}, ToolbarItemOrder.Default, 0);
|
|
|
|
|
|
2017-06-27 23:18:32 +03:00
|
|
|
|
if(!_providerType.HasValue)
|
2016-07-23 09:17:11 +03:00
|
|
|
|
{
|
2017-06-29 19:11:07 +03:00
|
|
|
|
instruction.Text = AppResources.NoTwoStepAvailable;
|
2017-06-29 05:24:04 +03:00
|
|
|
|
|
|
|
|
|
var layout = new StackLayout
|
2017-06-27 23:18:32 +03:00
|
|
|
|
{
|
2017-06-29 05:24:04 +03:00
|
|
|
|
Children = { instruction, anotherMethodButton },
|
|
|
|
|
Spacing = 0
|
2017-06-27 23:18:32 +03:00
|
|
|
|
};
|
2017-06-29 05:24:04 +03:00
|
|
|
|
|
|
|
|
|
scrollView.Content = layout;
|
|
|
|
|
|
2017-06-29 19:11:07 +03:00
|
|
|
|
Title = AppResources.LoginUnavailable;
|
2017-06-29 05:24:04 +03:00
|
|
|
|
Content = scrollView;
|
2017-06-27 23:18:32 +03:00
|
|
|
|
}
|
2017-06-28 17:09:52 +03:00
|
|
|
|
else if(_providerType.Value == TwoFactorProviderType.Authenticator ||
|
|
|
|
|
_providerType.Value == TwoFactorProviderType.Email)
|
2017-06-27 23:18:32 +03:00
|
|
|
|
{
|
|
|
|
|
var padding = Helpers.OnPlatform(
|
|
|
|
|
iOS: new Thickness(15, 20),
|
|
|
|
|
Android: new Thickness(15, 8),
|
2017-12-11 07:26:58 +03:00
|
|
|
|
Windows: new Thickness(10, 8));
|
2017-06-27 23:18:32 +03:00
|
|
|
|
|
|
|
|
|
TokenCell = new FormEntryCell(AppResources.VerificationCode, useLabelAsPlaceholder: true,
|
|
|
|
|
imageSource: "lock", containerPadding: padding);
|
|
|
|
|
|
|
|
|
|
TokenCell.Entry.Keyboard = Keyboard.Numeric;
|
|
|
|
|
TokenCell.Entry.ReturnType = ReturnType.Go;
|
|
|
|
|
|
2017-06-29 05:24:04 +03:00
|
|
|
|
var table = new TwoFactorTable(
|
2017-11-21 06:39:49 +03:00
|
|
|
|
new TableSection(Helpers.GetEmptyTableSectionTitle())
|
2016-07-23 09:17:11 +03:00
|
|
|
|
{
|
2017-06-29 05:24:04 +03:00
|
|
|
|
TokenCell,
|
|
|
|
|
RememberCell
|
|
|
|
|
});
|
2016-07-23 09:17:11 +03:00
|
|
|
|
|
2018-01-03 20:18:15 +03:00
|
|
|
|
var layout = new RedrawableStackLayout
|
2017-06-27 23:18:32 +03:00
|
|
|
|
{
|
2017-06-29 18:22:06 +03:00
|
|
|
|
Children = { instruction, table },
|
2017-06-27 23:18:32 +03:00
|
|
|
|
Spacing = 0
|
|
|
|
|
};
|
2016-07-23 09:17:11 +03:00
|
|
|
|
|
2018-01-03 20:18:15 +03:00
|
|
|
|
table.WrappingStackLayout = () => layout;
|
2017-06-27 23:18:32 +03:00
|
|
|
|
scrollView.Content = layout;
|
|
|
|
|
|
|
|
|
|
switch(_providerType.Value)
|
|
|
|
|
{
|
|
|
|
|
case TwoFactorProviderType.Authenticator:
|
2017-06-29 19:11:07 +03:00
|
|
|
|
instruction.Text = AppResources.EnterVerificationCodeApp;
|
2017-06-27 23:18:32 +03:00
|
|
|
|
layout.Children.Add(anotherMethodButton);
|
|
|
|
|
break;
|
|
|
|
|
case TwoFactorProviderType.Email:
|
|
|
|
|
var emailParams = _providers[TwoFactorProviderType.Email];
|
|
|
|
|
var redactedEmail = emailParams["Email"].ToString();
|
|
|
|
|
|
2017-06-29 19:11:07 +03:00
|
|
|
|
instruction.Text = string.Format(AppResources.EnterVerificationCodeEmail, redactedEmail);
|
2017-06-27 23:18:32 +03:00
|
|
|
|
var resendEmailButton = new ExtendedButton
|
|
|
|
|
{
|
2017-06-29 19:11:07 +03:00
|
|
|
|
Text = AppResources.SendVerificationCodeAgain,
|
2017-06-27 23:18:32 +03:00
|
|
|
|
Style = (Style)Application.Current.Resources["btn-primaryAccent"],
|
2017-06-29 18:22:06 +03:00
|
|
|
|
Margin = new Thickness(15, 0, 15, 0),
|
|
|
|
|
Command = new Command(async () => await SendEmailAsync(true)),
|
2017-06-27 23:18:32 +03:00
|
|
|
|
Uppercase = false,
|
2017-06-29 18:22:06 +03:00
|
|
|
|
BackgroundColor = Color.Transparent,
|
|
|
|
|
VerticalOptions = LayoutOptions.Start
|
2017-06-27 23:18:32 +03:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
layout.Children.Add(resendEmailButton);
|
|
|
|
|
layout.Children.Add(anotherMethodButton);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
2017-06-28 17:09:52 +03:00
|
|
|
|
|
|
|
|
|
ToolbarItems.Add(continueToolbarItem);
|
|
|
|
|
Title = AppResources.VerificationCode;
|
|
|
|
|
|
|
|
|
|
Content = scrollView;
|
2017-06-29 19:11:07 +03:00
|
|
|
|
TokenCell.Entry.FocusWithDelay();
|
2017-06-27 23:18:32 +03:00
|
|
|
|
}
|
2018-04-03 22:59:58 +03:00
|
|
|
|
else if(_providerType == TwoFactorProviderType.Duo ||
|
|
|
|
|
_providerType == TwoFactorProviderType.OrganizationDuo)
|
2017-06-28 17:09:52 +03:00
|
|
|
|
{
|
2018-04-03 22:59:58 +03:00
|
|
|
|
var duoParams = _providers[_providerType.Value];
|
2017-06-28 17:09:52 +03:00
|
|
|
|
|
|
|
|
|
var host = WebUtility.UrlEncode(duoParams["Host"].ToString());
|
|
|
|
|
var req = WebUtility.UrlEncode(duoParams["Signature"].ToString());
|
2016-07-23 09:17:11 +03:00
|
|
|
|
|
2017-08-29 01:08:26 +03:00
|
|
|
|
var webVaultUrl = "https://vault.bitwarden.com";
|
2017-08-29 00:50:17 +03:00
|
|
|
|
if(!string.IsNullOrWhiteSpace(_appSettingsService.BaseUrl))
|
|
|
|
|
{
|
2017-08-29 01:08:26 +03:00
|
|
|
|
webVaultUrl = _appSettingsService.BaseUrl;
|
2017-08-29 00:50:17 +03:00
|
|
|
|
}
|
2017-08-29 01:08:26 +03:00
|
|
|
|
else if(!string.IsNullOrWhiteSpace(_appSettingsService.WebVaultUrl))
|
2017-08-29 00:50:17 +03:00
|
|
|
|
{
|
2017-08-29 01:08:26 +03:00
|
|
|
|
webVaultUrl = _appSettingsService.WebVaultUrl;
|
2017-08-29 00:50:17 +03:00
|
|
|
|
}
|
|
|
|
|
|
2017-06-29 05:24:04 +03:00
|
|
|
|
var webView = new HybridWebView
|
2017-06-28 17:09:52 +03:00
|
|
|
|
{
|
2017-08-29 01:08:26 +03:00
|
|
|
|
Uri = $"{webVaultUrl}/duo-connector.html?host={host}&request={req}",
|
2017-06-28 17:09:52 +03:00
|
|
|
|
HorizontalOptions = LayoutOptions.FillAndExpand,
|
2017-06-29 18:22:06 +03:00
|
|
|
|
VerticalOptions = LayoutOptions.FillAndExpand,
|
|
|
|
|
MinimumHeightRequest = 400
|
2017-06-28 17:09:52 +03:00
|
|
|
|
};
|
2017-06-29 05:24:04 +03:00
|
|
|
|
webView.RegisterAction(async (sig) =>
|
2017-06-28 17:09:52 +03:00
|
|
|
|
{
|
2017-06-29 18:22:06 +03:00
|
|
|
|
await LogInAsync(sig);
|
2017-06-28 17:09:52 +03:00
|
|
|
|
});
|
|
|
|
|
|
2017-06-29 18:22:06 +03:00
|
|
|
|
var table = new TwoFactorTable(
|
2017-11-21 06:39:49 +03:00
|
|
|
|
new TableSection(Helpers.GetEmptyTableSectionTitle())
|
2017-06-29 18:22:06 +03:00
|
|
|
|
{
|
|
|
|
|
RememberCell
|
|
|
|
|
});
|
|
|
|
|
|
2018-01-03 20:18:15 +03:00
|
|
|
|
var layout = new RedrawableStackLayout
|
2017-06-29 18:22:06 +03:00
|
|
|
|
{
|
|
|
|
|
Children = { webView, table, anotherMethodButton },
|
|
|
|
|
Spacing = 0
|
|
|
|
|
};
|
|
|
|
|
|
2018-01-03 20:18:15 +03:00
|
|
|
|
table.WrappingStackLayout = () => layout;
|
2017-06-29 18:22:06 +03:00
|
|
|
|
scrollView.Content = layout;
|
|
|
|
|
|
2018-04-03 22:59:58 +03:00
|
|
|
|
Title = _providerType == TwoFactorProviderType.Duo ? "Duo" : _duoOrgTitle;
|
2017-06-29 18:22:06 +03:00
|
|
|
|
Content = scrollView;
|
2017-06-29 05:24:04 +03:00
|
|
|
|
}
|
|
|
|
|
else if(_providerType == TwoFactorProviderType.YubiKey)
|
|
|
|
|
{
|
2017-06-29 19:11:07 +03:00
|
|
|
|
instruction.Text = AppResources.YubiKeyInstruction;
|
2017-06-29 05:24:04 +03:00
|
|
|
|
|
|
|
|
|
var image = new CachedImage
|
|
|
|
|
{
|
2018-01-18 15:55:06 +03:00
|
|
|
|
Source = "yubikey.png",
|
2017-06-29 05:24:04 +03:00
|
|
|
|
VerticalOptions = LayoutOptions.Start,
|
|
|
|
|
HorizontalOptions = LayoutOptions.Center,
|
|
|
|
|
WidthRequest = 266,
|
|
|
|
|
HeightRequest = 160,
|
|
|
|
|
Margin = new Thickness(0, 0, 0, 25)
|
|
|
|
|
};
|
|
|
|
|
|
2018-05-25 05:40:02 +03:00
|
|
|
|
var section = new TableSection(Helpers.GetEmptyTableSectionTitle())
|
|
|
|
|
{
|
|
|
|
|
RememberCell
|
|
|
|
|
};
|
2018-01-18 15:55:06 +03:00
|
|
|
|
|
2018-05-25 05:40:02 +03:00
|
|
|
|
if(Device.RuntimePlatform != Device.iOS)
|
|
|
|
|
{
|
|
|
|
|
TokenCell = new FormEntryCell("", isPassword: true, imageSource: "lock",
|
|
|
|
|
useLabelAsPlaceholder: true);
|
|
|
|
|
TokenCell.Entry.ReturnType = ReturnType.Go;
|
|
|
|
|
section.Insert(0, TokenCell);
|
|
|
|
|
}
|
2017-06-29 05:24:04 +03:00
|
|
|
|
|
2018-05-25 05:40:02 +03:00
|
|
|
|
var table = new TwoFactorTable(section);
|
2018-01-03 20:18:15 +03:00
|
|
|
|
var layout = new RedrawableStackLayout
|
2017-06-29 05:24:04 +03:00
|
|
|
|
{
|
|
|
|
|
Children = { instruction, image, table, anotherMethodButton },
|
|
|
|
|
Spacing = 0
|
|
|
|
|
};
|
|
|
|
|
|
2018-05-25 05:43:59 +03:00
|
|
|
|
if(Device.RuntimePlatform == Device.iOS)
|
|
|
|
|
{
|
|
|
|
|
var tryAgainButton = new ExtendedButton
|
|
|
|
|
{
|
|
|
|
|
Text = AppResources.UseAnotherTwoStepMethod,
|
|
|
|
|
Style = (Style)Application.Current.Resources["btn-primaryAccent"],
|
|
|
|
|
Margin = new Thickness(15, 0, 15, 25),
|
|
|
|
|
Command = new Command(() => ListenYubiKey(true, true)),
|
|
|
|
|
Uppercase = false,
|
|
|
|
|
BackgroundColor = Color.Transparent,
|
|
|
|
|
VerticalOptions = LayoutOptions.Start
|
|
|
|
|
};
|
|
|
|
|
layout.Children.Insert(3, tryAgainButton);
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-03 20:18:15 +03:00
|
|
|
|
table.WrappingStackLayout = () => layout;
|
2017-06-29 05:24:04 +03:00
|
|
|
|
scrollView.Content = layout;
|
2018-01-18 15:55:06 +03:00
|
|
|
|
ToolbarItems.Add(continueToolbarItem);
|
2017-06-29 05:24:04 +03:00
|
|
|
|
|
2017-06-29 19:11:07 +03:00
|
|
|
|
Title = AppResources.YubiKeyTitle;
|
2017-06-29 05:24:04 +03:00
|
|
|
|
Content = scrollView;
|
2017-06-28 17:09:52 +03:00
|
|
|
|
}
|
2016-07-23 09:17:11 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected override void OnAppearing()
|
|
|
|
|
{
|
|
|
|
|
base.OnAppearing();
|
2017-06-29 05:24:04 +03:00
|
|
|
|
ListenYubiKey(true);
|
2017-06-27 23:18:32 +03:00
|
|
|
|
|
2017-07-24 18:06:55 +03:00
|
|
|
|
InitEvents();
|
|
|
|
|
if(TokenCell == null && Device.RuntimePlatform == Device.Android)
|
|
|
|
|
{
|
2017-11-22 07:08:45 +03:00
|
|
|
|
_deviceActionService.DismissKeyboard();
|
2017-07-24 18:06:55 +03:00
|
|
|
|
}
|
2018-01-18 15:55:06 +03:00
|
|
|
|
|
|
|
|
|
if(TokenCell != null)
|
|
|
|
|
{
|
|
|
|
|
TokenCell.Entry.FocusWithDelay();
|
|
|
|
|
}
|
2017-07-24 18:06:55 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void InitEvents()
|
|
|
|
|
{
|
2017-06-27 23:18:32 +03:00
|
|
|
|
if(TokenCell != null)
|
|
|
|
|
{
|
|
|
|
|
TokenCell.InitEvents();
|
|
|
|
|
TokenCell.Entry.Completed += Entry_Completed;
|
|
|
|
|
}
|
2017-02-15 08:28:05 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected override void OnDisappearing()
|
|
|
|
|
{
|
|
|
|
|
base.OnDisappearing();
|
2017-06-29 05:24:04 +03:00
|
|
|
|
ListenYubiKey(false);
|
2017-06-27 23:18:32 +03:00
|
|
|
|
|
|
|
|
|
if(TokenCell != null)
|
|
|
|
|
{
|
|
|
|
|
TokenCell.Dispose();
|
|
|
|
|
TokenCell.Entry.Completed -= Entry_Completed;
|
|
|
|
|
}
|
2018-01-18 21:24:23 +03:00
|
|
|
|
|
|
|
|
|
MessagingCenter.Unsubscribe<Application, string>(Application.Current, "GotYubiKeyOTP");
|
|
|
|
|
MessagingCenter.Unsubscribe<Application>(Application.Current, "ResumeYubiKey");
|
2017-06-27 23:18:32 +03:00
|
|
|
|
}
|
|
|
|
|
|
2017-06-28 00:10:40 +03:00
|
|
|
|
private async void AnotherMethodAsync()
|
2017-06-27 23:18:32 +03:00
|
|
|
|
{
|
2017-06-29 18:22:06 +03:00
|
|
|
|
var beforeProviderType = _providerType;
|
2017-06-27 23:18:32 +03:00
|
|
|
|
|
2017-06-29 18:22:06 +03:00
|
|
|
|
var options = new List<string>();
|
2018-04-03 22:59:58 +03:00
|
|
|
|
if(_providers.ContainsKey(TwoFactorProviderType.OrganizationDuo))
|
|
|
|
|
{
|
|
|
|
|
options.Add(_duoOrgTitle);
|
|
|
|
|
}
|
|
|
|
|
|
2017-06-29 18:22:06 +03:00
|
|
|
|
if(_providers.ContainsKey(TwoFactorProviderType.Authenticator))
|
|
|
|
|
{
|
2017-06-29 19:11:07 +03:00
|
|
|
|
options.Add(AppResources.AuthenticatorAppTitle);
|
2017-06-29 18:22:06 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(_providers.ContainsKey(TwoFactorProviderType.Duo))
|
|
|
|
|
{
|
|
|
|
|
options.Add("Duo");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(_providers.ContainsKey(TwoFactorProviderType.YubiKey))
|
|
|
|
|
{
|
|
|
|
|
var nfcKey = _providers[TwoFactorProviderType.YubiKey].ContainsKey("Nfc") &&
|
|
|
|
|
(bool)_providers[TwoFactorProviderType.YubiKey]["Nfc"];
|
2018-01-18 15:55:06 +03:00
|
|
|
|
if(_deviceInfoService.NfcEnabled && nfcKey || Device.RuntimePlatform == Device.UWP)
|
2017-06-29 18:22:06 +03:00
|
|
|
|
{
|
2017-06-29 19:11:07 +03:00
|
|
|
|
options.Add(AppResources.YubiKeyTitle);
|
2017-06-29 18:22:06 +03:00
|
|
|
|
}
|
|
|
|
|
}
|
2017-06-27 23:18:32 +03:00
|
|
|
|
|
2017-06-29 18:22:06 +03:00
|
|
|
|
if(_providers.ContainsKey(TwoFactorProviderType.Email))
|
|
|
|
|
{
|
2017-06-29 19:11:07 +03:00
|
|
|
|
options.Add(AppResources.Email);
|
2017-06-29 18:22:06 +03:00
|
|
|
|
}
|
|
|
|
|
|
2017-06-29 19:11:07 +03:00
|
|
|
|
options.Add(AppResources.RecoveryCodeTitle);
|
2017-06-29 18:22:06 +03:00
|
|
|
|
|
2017-06-29 19:11:07 +03:00
|
|
|
|
var selection = await DisplayActionSheet(AppResources.TwoStepLoginOptions, AppResources.Cancel, null,
|
|
|
|
|
options.ToArray());
|
|
|
|
|
if(selection == AppResources.AuthenticatorAppTitle)
|
2017-06-29 18:22:06 +03:00
|
|
|
|
{
|
|
|
|
|
_providerType = TwoFactorProviderType.Authenticator;
|
|
|
|
|
}
|
|
|
|
|
else if(selection == "Duo")
|
|
|
|
|
{
|
|
|
|
|
_providerType = TwoFactorProviderType.Duo;
|
|
|
|
|
}
|
2018-04-03 22:59:58 +03:00
|
|
|
|
else if(selection == _duoOrgTitle)
|
|
|
|
|
{
|
|
|
|
|
_providerType = TwoFactorProviderType.OrganizationDuo;
|
|
|
|
|
}
|
2017-06-29 19:11:07 +03:00
|
|
|
|
else if(selection == AppResources.YubiKeyTitle)
|
2017-06-29 18:22:06 +03:00
|
|
|
|
{
|
|
|
|
|
_providerType = TwoFactorProviderType.YubiKey;
|
|
|
|
|
}
|
2017-06-29 19:11:07 +03:00
|
|
|
|
else if(selection == AppResources.Email)
|
2017-06-29 18:22:06 +03:00
|
|
|
|
{
|
|
|
|
|
_providerType = TwoFactorProviderType.Email;
|
|
|
|
|
}
|
2017-06-29 19:11:07 +03:00
|
|
|
|
else if(selection == AppResources.RecoveryCodeTitle)
|
2017-06-29 18:22:06 +03:00
|
|
|
|
{
|
|
|
|
|
Device.OpenUri(new Uri("https://help.bitwarden.com/article/lost-two-step-device/"));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(beforeProviderType != _providerType)
|
|
|
|
|
{
|
|
|
|
|
Init();
|
|
|
|
|
ListenYubiKey(false, beforeProviderType == TwoFactorProviderType.YubiKey);
|
|
|
|
|
ListenYubiKey(true);
|
2017-07-24 18:06:55 +03:00
|
|
|
|
InitEvents();
|
2017-06-29 18:22:06 +03:00
|
|
|
|
}
|
2016-07-23 09:17:11 +03:00
|
|
|
|
}
|
|
|
|
|
|
2017-06-29 18:22:06 +03:00
|
|
|
|
private async Task SendEmailAsync(bool doToast)
|
2016-11-26 09:03:02 +03:00
|
|
|
|
{
|
2017-06-29 18:22:06 +03:00
|
|
|
|
if(_providerType != TwoFactorProviderType.Email)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var response = await _twoFactorApiRepository.PostSendEmailLoginAsync(new Models.Api.TwoFactorEmailRequest
|
|
|
|
|
{
|
|
|
|
|
Email = _email,
|
|
|
|
|
MasterPasswordHash = _masterPasswordHash
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if(response.Succeeded && doToast)
|
|
|
|
|
{
|
2017-12-22 23:00:11 +03:00
|
|
|
|
_deviceActionService.Toast(AppResources.VerificationEmailSent);
|
2017-06-29 18:22:06 +03:00
|
|
|
|
}
|
|
|
|
|
else if(!response.Succeeded)
|
|
|
|
|
{
|
2017-12-23 06:41:48 +03:00
|
|
|
|
await DisplayAlert(null, AppResources.VerificationEmailNotSent, AppResources.Ok);
|
2017-06-29 18:22:06 +03:00
|
|
|
|
}
|
2016-11-26 09:03:02 +03:00
|
|
|
|
}
|
|
|
|
|
|
2016-07-23 09:17:11 +03:00
|
|
|
|
private async void Entry_Completed(object sender, EventArgs e)
|
|
|
|
|
{
|
2017-06-27 23:18:32 +03:00
|
|
|
|
var token = TokenCell.Entry.Text.Trim().Replace(" ", "");
|
2017-06-29 18:22:06 +03:00
|
|
|
|
await LogInAsync(token);
|
2016-07-23 09:17:11 +03:00
|
|
|
|
}
|
|
|
|
|
|
2017-06-29 18:22:06 +03:00
|
|
|
|
private async Task LogInAsync(string token)
|
2016-07-23 09:17:11 +03:00
|
|
|
|
{
|
2017-06-29 18:22:06 +03:00
|
|
|
|
if(!_providerType.HasValue || _lastAction.LastActionWasRecent())
|
2017-06-29 05:24:04 +03:00
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
_lastAction = DateTime.UtcNow;
|
|
|
|
|
|
2017-06-27 23:18:32 +03:00
|
|
|
|
if(string.IsNullOrWhiteSpace(token))
|
2016-07-23 09:17:11 +03:00
|
|
|
|
{
|
2016-08-17 02:20:41 +03:00
|
|
|
|
await DisplayAlert(AppResources.AnErrorHasOccurred, string.Format(AppResources.ValidationFieldRequired,
|
2016-11-26 00:54:33 +03:00
|
|
|
|
AppResources.VerificationCode), AppResources.Ok);
|
2016-07-23 09:17:11 +03:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-22 18:07:41 +03:00
|
|
|
|
await _deviceActionService.ShowLoadingAsync(string.Concat(AppResources.Validating, "..."));
|
2017-06-29 18:22:06 +03:00
|
|
|
|
var response = await _authService.TokenPostTwoFactorAsync(_providerType.Value, token, RememberCell.On,
|
2017-06-27 23:18:32 +03:00
|
|
|
|
_email, _masterPasswordHash, _key);
|
2018-03-22 18:07:41 +03:00
|
|
|
|
await _deviceActionService.HideLoadingAsync();
|
2017-12-23 06:41:48 +03:00
|
|
|
|
|
2017-04-20 05:04:43 +03:00
|
|
|
|
if(!response.Success)
|
2016-07-23 09:17:11 +03:00
|
|
|
|
{
|
2017-06-29 05:24:04 +03:00
|
|
|
|
ListenYubiKey(true);
|
2017-04-20 05:04:43 +03:00
|
|
|
|
await DisplayAlert(AppResources.AnErrorHasOccurred, response.ErrorMessage, AppResources.Ok);
|
2016-07-23 09:17:11 +03:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2017-06-29 18:22:06 +03:00
|
|
|
|
_googleAnalyticsService.TrackAppEvent("LoggedIn From Two-step", _providerType.Value.ToString());
|
2017-02-06 17:39:07 +03:00
|
|
|
|
|
2017-02-07 07:40:24 +03:00
|
|
|
|
var task = Task.Run(async () => await _syncService.FullSyncAsync(true));
|
2017-06-28 17:09:52 +03:00
|
|
|
|
Device.BeginInvokeOnMainThread(() =>
|
|
|
|
|
{
|
|
|
|
|
Application.Current.MainPage = new MainPage();
|
|
|
|
|
});
|
2016-07-23 09:17:11 +03:00
|
|
|
|
}
|
2017-06-27 23:18:32 +03:00
|
|
|
|
|
|
|
|
|
private TwoFactorProviderType? GetDefaultProvider()
|
|
|
|
|
{
|
|
|
|
|
TwoFactorProviderType? provider = null;
|
|
|
|
|
|
|
|
|
|
if(_providers != null)
|
|
|
|
|
{
|
|
|
|
|
foreach(var p in _providers)
|
|
|
|
|
{
|
|
|
|
|
switch(p.Key)
|
|
|
|
|
{
|
|
|
|
|
case TwoFactorProviderType.Authenticator:
|
2018-04-03 22:59:58 +03:00
|
|
|
|
if(provider == TwoFactorProviderType.Duo || provider == TwoFactorProviderType.YubiKey ||
|
|
|
|
|
provider == TwoFactorProviderType.OrganizationDuo)
|
2017-06-27 23:18:32 +03:00
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case TwoFactorProviderType.Email:
|
|
|
|
|
if(provider.HasValue)
|
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case TwoFactorProviderType.Duo:
|
2018-04-03 22:59:58 +03:00
|
|
|
|
if(provider == TwoFactorProviderType.YubiKey ||
|
|
|
|
|
provider == TwoFactorProviderType.OrganizationDuo)
|
2017-06-29 05:24:04 +03:00
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case TwoFactorProviderType.YubiKey:
|
2018-04-03 22:59:58 +03:00
|
|
|
|
if(provider == TwoFactorProviderType.OrganizationDuo)
|
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2017-06-29 18:22:06 +03:00
|
|
|
|
var nfcKey = p.Value.ContainsKey("Nfc") && (bool)p.Value["Nfc"];
|
2018-01-18 15:55:06 +03:00
|
|
|
|
if((!_deviceInfoService.NfcEnabled || !nfcKey) && Device.RuntimePlatform != Device.UWP)
|
2017-06-29 05:24:04 +03:00
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2017-06-27 23:18:32 +03:00
|
|
|
|
break;
|
2018-04-03 22:59:58 +03:00
|
|
|
|
case TwoFactorProviderType.OrganizationDuo:
|
|
|
|
|
break;
|
2017-06-27 23:18:32 +03:00
|
|
|
|
default:
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
provider = p.Key;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return provider;
|
|
|
|
|
}
|
2017-06-29 05:24:04 +03:00
|
|
|
|
|
2017-06-29 18:22:06 +03:00
|
|
|
|
private void ListenYubiKey(bool listen, bool overrideCheck = false)
|
2017-06-29 05:24:04 +03:00
|
|
|
|
{
|
2017-06-29 18:22:06 +03:00
|
|
|
|
if(_providerType == TwoFactorProviderType.YubiKey || overrideCheck)
|
2017-06-29 05:24:04 +03:00
|
|
|
|
{
|
|
|
|
|
MessagingCenter.Send(Application.Current, "ListenYubiKeyOTP", listen);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void SubscribeYubiKey(bool subscribe)
|
|
|
|
|
{
|
|
|
|
|
if(_providerType != TwoFactorProviderType.YubiKey)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MessagingCenter.Unsubscribe<Application, string>(Application.Current, "GotYubiKeyOTP");
|
|
|
|
|
MessagingCenter.Unsubscribe<Application>(Application.Current, "ResumeYubiKey");
|
|
|
|
|
if(!subscribe)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MessagingCenter.Subscribe<Application, string>(Application.Current, "GotYubiKeyOTP", async (sender, otp) =>
|
|
|
|
|
{
|
|
|
|
|
MessagingCenter.Unsubscribe<Application, string>(Application.Current, "GotYubiKeyOTP");
|
|
|
|
|
if(_providerType == TwoFactorProviderType.YubiKey)
|
|
|
|
|
{
|
2017-06-29 18:22:06 +03:00
|
|
|
|
await LogInAsync(otp);
|
2017-06-29 05:24:04 +03:00
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
SubscribeYubiKeyResume();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void SubscribeYubiKeyResume()
|
|
|
|
|
{
|
|
|
|
|
MessagingCenter.Subscribe<Application>(Application.Current, "ResumeYubiKey", (sender) =>
|
|
|
|
|
{
|
|
|
|
|
MessagingCenter.Unsubscribe<Application>(Application.Current, "ResumeYubiKey");
|
|
|
|
|
if(_providerType == TwoFactorProviderType.YubiKey)
|
|
|
|
|
{
|
|
|
|
|
MessagingCenter.Send(Application.Current, "ListenYubiKeyOTP", true);
|
|
|
|
|
SubscribeYubiKeyResume();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public class TwoFactorTable : ExtendedTableView
|
|
|
|
|
{
|
|
|
|
|
public TwoFactorTable(TableSection section)
|
|
|
|
|
{
|
|
|
|
|
Intent = TableIntent.Settings;
|
|
|
|
|
EnableScrolling = false;
|
|
|
|
|
HasUnevenRows = true;
|
|
|
|
|
EnableSelection = true;
|
|
|
|
|
NoFooter = true;
|
|
|
|
|
NoHeader = true;
|
|
|
|
|
VerticalOptions = LayoutOptions.Start;
|
2018-01-03 20:18:15 +03:00
|
|
|
|
Root = new TableRoot
|
2017-06-29 05:24:04 +03:00
|
|
|
|
{
|
|
|
|
|
section
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if(Device.RuntimePlatform == Device.iOS)
|
|
|
|
|
{
|
|
|
|
|
RowHeight = -1;
|
|
|
|
|
EstimatedRowHeight = 70;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-07-23 09:17:11 +03:00
|
|
|
|
}
|
|
|
|
|
}
|