mirror of
https://github.com/bitwarden/android.git
synced 2024-12-24 18:08:26 +03:00
two-factor other methods switching and send email
This commit is contained in:
parent
56075cb7d9
commit
74fba486bd
11 changed files with 208 additions and 188 deletions
|
@ -51,7 +51,7 @@ namespace Bit.Android
|
||||||
|
|
||||||
Console.WriteLine("A OnCreate");
|
Console.WriteLine("A OnCreate");
|
||||||
Window.SetSoftInputMode(SoftInput.StateHidden);
|
Window.SetSoftInputMode(SoftInput.StateHidden);
|
||||||
//Window.AddFlags(WindowManagerFlags.Secure);
|
Window.AddFlags(WindowManagerFlags.Secure);
|
||||||
|
|
||||||
var appIdService = Resolver.Resolve<IAppIdService>();
|
var appIdService = Resolver.Resolve<IAppIdService>();
|
||||||
var authService = Resolver.Resolve<IAuthService>();
|
var authService = Resolver.Resolve<IAuthService>();
|
||||||
|
|
|
@ -234,6 +234,7 @@ namespace Bit.Android
|
||||||
container.RegisterSingleton<ICipherApiRepository, CipherApiRepository>();
|
container.RegisterSingleton<ICipherApiRepository, CipherApiRepository>();
|
||||||
container.RegisterSingleton<ISettingsRepository, SettingsRepository>();
|
container.RegisterSingleton<ISettingsRepository, SettingsRepository>();
|
||||||
container.RegisterSingleton<ISettingsApiRepository, SettingsApiRepository>();
|
container.RegisterSingleton<ISettingsApiRepository, SettingsApiRepository>();
|
||||||
|
container.RegisterSingleton<ITwoFactorApiRepository, TwoFactorApiRepository>();
|
||||||
|
|
||||||
// Other
|
// Other
|
||||||
container.RegisterSingleton(CrossSettings.Current);
|
container.RegisterSingleton(CrossSettings.Current);
|
||||||
|
|
10
src/App/Abstractions/Repositories/ITwoFactorApiRepository.cs
Normal file
10
src/App/Abstractions/Repositories/ITwoFactorApiRepository.cs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Bit.App.Models.Api;
|
||||||
|
|
||||||
|
namespace Bit.App.Abstractions
|
||||||
|
{
|
||||||
|
public interface ITwoFactorApiRepository
|
||||||
|
{
|
||||||
|
Task<ApiResult> PostSendEmailLoginAsync(TwoFactorEmailRequest requestObj);
|
||||||
|
}
|
||||||
|
}
|
|
@ -35,6 +35,7 @@
|
||||||
<WarningLevel>4</WarningLevel>
|
<WarningLevel>4</WarningLevel>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Compile Include="Abstractions\Repositories\ITwoFactorApiRepository.cs" />
|
||||||
<Compile Include="Abstractions\Repositories\ISettingsApiRepository.cs" />
|
<Compile Include="Abstractions\Repositories\ISettingsApiRepository.cs" />
|
||||||
<Compile Include="Abstractions\Repositories\IAccountsApiRepository.cs" />
|
<Compile Include="Abstractions\Repositories\IAccountsApiRepository.cs" />
|
||||||
<Compile Include="Abstractions\Repositories\IDeviceApiRepository.cs" />
|
<Compile Include="Abstractions\Repositories\IDeviceApiRepository.cs" />
|
||||||
|
@ -97,6 +98,7 @@
|
||||||
<Compile Include="Models\Api\Request\DeviceTokenRequest.cs" />
|
<Compile Include="Models\Api\Request\DeviceTokenRequest.cs" />
|
||||||
<Compile Include="Models\Api\Request\FolderRequest.cs" />
|
<Compile Include="Models\Api\Request\FolderRequest.cs" />
|
||||||
<Compile Include="Models\Api\Request\DeviceRequest.cs" />
|
<Compile Include="Models\Api\Request\DeviceRequest.cs" />
|
||||||
|
<Compile Include="Models\Api\Request\TwoFactorEmailRequest.cs" />
|
||||||
<Compile Include="Models\Api\Request\RegisterRequest.cs" />
|
<Compile Include="Models\Api\Request\RegisterRequest.cs" />
|
||||||
<Compile Include="Models\Api\Request\LoginRequest.cs" />
|
<Compile Include="Models\Api\Request\LoginRequest.cs" />
|
||||||
<Compile Include="Models\Api\Request\PasswordHintRequest.cs" />
|
<Compile Include="Models\Api\Request\PasswordHintRequest.cs" />
|
||||||
|
@ -135,7 +137,6 @@
|
||||||
<Compile Include="Pages\HomePage.cs" />
|
<Compile Include="Pages\HomePage.cs" />
|
||||||
<Compile Include="Pages\Lock\BaseLockPage.cs" />
|
<Compile Include="Pages\Lock\BaseLockPage.cs" />
|
||||||
<Compile Include="Pages\Lock\LockPasswordPage.cs" />
|
<Compile Include="Pages\Lock\LockPasswordPage.cs" />
|
||||||
<Compile Include="Pages\TwoFactorMethodsPage.cs" />
|
|
||||||
<Compile Include="Pages\LoginTwoFactorPage.cs" />
|
<Compile Include="Pages\LoginTwoFactorPage.cs" />
|
||||||
<Compile Include="Pages\PasswordHintPage.cs" />
|
<Compile Include="Pages\PasswordHintPage.cs" />
|
||||||
<Compile Include="Pages\RegisterPage.cs" />
|
<Compile Include="Pages\RegisterPage.cs" />
|
||||||
|
@ -159,6 +160,7 @@
|
||||||
<Compile Include="Pages\Vault\VaultAutofillListLoginsPage.cs" />
|
<Compile Include="Pages\Vault\VaultAutofillListLoginsPage.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
<Compile Include="Abstractions\Repositories\ILoginRepository.cs" />
|
<Compile Include="Abstractions\Repositories\ILoginRepository.cs" />
|
||||||
|
<Compile Include="Repositories\TwoFactorApiRepository.cs" />
|
||||||
<Compile Include="Repositories\SettingsApiRepository.cs" />
|
<Compile Include="Repositories\SettingsApiRepository.cs" />
|
||||||
<Compile Include="Repositories\ApiRepository.cs" />
|
<Compile Include="Repositories\ApiRepository.cs" />
|
||||||
<Compile Include="Repositories\AccountsApiRepository.cs" />
|
<Compile Include="Repositories\AccountsApiRepository.cs" />
|
||||||
|
|
8
src/App/Models/Api/Request/TwoFactorEmailRequest.cs
Normal file
8
src/App/Models/Api/Request/TwoFactorEmailRequest.cs
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
namespace Bit.App.Models.Api
|
||||||
|
{
|
||||||
|
public class TwoFactorEmailRequest
|
||||||
|
{
|
||||||
|
public string Email { get; set; }
|
||||||
|
public string MasterPasswordHash { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,7 +11,6 @@ using Bit.App.Models;
|
||||||
using Bit.App.Utilities;
|
using Bit.App.Utilities;
|
||||||
using Bit.App.Enums;
|
using Bit.App.Enums;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using FFImageLoading.Forms;
|
using FFImageLoading.Forms;
|
||||||
|
|
||||||
|
@ -25,12 +24,13 @@ namespace Bit.App.Pages
|
||||||
private ISyncService _syncService;
|
private ISyncService _syncService;
|
||||||
private IDeviceInfoService _deviceInfoService;
|
private IDeviceInfoService _deviceInfoService;
|
||||||
private IGoogleAnalyticsService _googleAnalyticsService;
|
private IGoogleAnalyticsService _googleAnalyticsService;
|
||||||
|
private ITwoFactorApiRepository _twoFactorApiRepository;
|
||||||
private IPushNotification _pushNotification;
|
private IPushNotification _pushNotification;
|
||||||
private readonly string _email;
|
private readonly string _email;
|
||||||
private readonly string _masterPasswordHash;
|
private readonly string _masterPasswordHash;
|
||||||
private readonly SymmetricCryptoKey _key;
|
private readonly SymmetricCryptoKey _key;
|
||||||
private readonly Dictionary<TwoFactorProviderType, Dictionary<string, object>> _providers;
|
private readonly Dictionary<TwoFactorProviderType, Dictionary<string, object>> _providers;
|
||||||
private readonly TwoFactorProviderType? _providerType;
|
private TwoFactorProviderType? _providerType;
|
||||||
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)
|
||||||
|
@ -49,11 +49,10 @@ namespace Bit.App.Pages
|
||||||
_userDialogs = Resolver.Resolve<IUserDialogs>();
|
_userDialogs = Resolver.Resolve<IUserDialogs>();
|
||||||
_syncService = Resolver.Resolve<ISyncService>();
|
_syncService = Resolver.Resolve<ISyncService>();
|
||||||
_googleAnalyticsService = Resolver.Resolve<IGoogleAnalyticsService>();
|
_googleAnalyticsService = Resolver.Resolve<IGoogleAnalyticsService>();
|
||||||
|
_twoFactorApiRepository = Resolver.Resolve<ITwoFactorApiRepository>();
|
||||||
_pushNotification = Resolver.Resolve<IPushNotification>();
|
_pushNotification = Resolver.Resolve<IPushNotification>();
|
||||||
|
|
||||||
Init();
|
Init();
|
||||||
|
|
||||||
SubscribeYubiKey(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public FormEntryCell TokenCell { get; set; }
|
public FormEntryCell TokenCell { get; set; }
|
||||||
|
@ -61,6 +60,13 @@ namespace Bit.App.Pages
|
||||||
|
|
||||||
private void Init()
|
private void Init()
|
||||||
{
|
{
|
||||||
|
SubscribeYubiKey(true);
|
||||||
|
if(_providers.Count > 1)
|
||||||
|
{
|
||||||
|
var sendEmailTask = SendEmailAsync(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
ToolbarItems.Clear();
|
||||||
var scrollView = new ScrollView();
|
var scrollView = new ScrollView();
|
||||||
|
|
||||||
var anotherMethodButton = new ExtendedButton
|
var anotherMethodButton = new ExtendedButton
|
||||||
|
@ -70,7 +76,8 @@ namespace Bit.App.Pages
|
||||||
Margin = new Thickness(15, 0, 15, 25),
|
Margin = new Thickness(15, 0, 15, 25),
|
||||||
Command = new Command(() => AnotherMethodAsync()),
|
Command = new Command(() => AnotherMethodAsync()),
|
||||||
Uppercase = false,
|
Uppercase = false,
|
||||||
BackgroundColor = Color.Transparent
|
BackgroundColor = Color.Transparent,
|
||||||
|
VerticalOptions = LayoutOptions.Start
|
||||||
};
|
};
|
||||||
|
|
||||||
var instruction = new Label
|
var instruction = new Label
|
||||||
|
@ -107,7 +114,7 @@ namespace Bit.App.Pages
|
||||||
var continueToolbarItem = new ToolbarItem(AppResources.Continue, null, async () =>
|
var continueToolbarItem = new ToolbarItem(AppResources.Continue, null, async () =>
|
||||||
{
|
{
|
||||||
var token = TokenCell?.Entry.Text.Trim().Replace(" ", "");
|
var token = TokenCell?.Entry.Text.Trim().Replace(" ", "");
|
||||||
await LogInAsync(token, RememberCell.On);
|
await LogInAsync(token);
|
||||||
}, ToolbarItemOrder.Default, 0);
|
}, ToolbarItemOrder.Default, 0);
|
||||||
|
|
||||||
var padding = Helpers.OnPlatform(
|
var padding = Helpers.OnPlatform(
|
||||||
|
@ -130,7 +137,7 @@ namespace Bit.App.Pages
|
||||||
|
|
||||||
var layout = new StackLayout
|
var layout = new StackLayout
|
||||||
{
|
{
|
||||||
Children = { instruction, table, anotherMethodButton },
|
Children = { instruction, table },
|
||||||
Spacing = 0
|
Spacing = 0
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -140,27 +147,24 @@ namespace Bit.App.Pages
|
||||||
{
|
{
|
||||||
case TwoFactorProviderType.Authenticator:
|
case TwoFactorProviderType.Authenticator:
|
||||||
instruction.Text = "Enter the 6 digit verification code from your authenticator app.";
|
instruction.Text = "Enter the 6 digit verification code from your authenticator app.";
|
||||||
layout.Children.Add(instruction);
|
|
||||||
layout.Children.Add(table);
|
|
||||||
layout.Children.Add(anotherMethodButton);
|
layout.Children.Add(anotherMethodButton);
|
||||||
break;
|
break;
|
||||||
case TwoFactorProviderType.Email:
|
case TwoFactorProviderType.Email:
|
||||||
var emailParams = _providers[TwoFactorProviderType.Email];
|
var emailParams = _providers[TwoFactorProviderType.Email];
|
||||||
var redactedEmail = emailParams["Email"].ToString();
|
var redactedEmail = emailParams["Email"].ToString();
|
||||||
|
|
||||||
instruction.Text = "Enter the 6 digit verification code from your authenticator app.";
|
instruction.Text = $"Enter the 6 digit verification code that was emailed to {redactedEmail}.";
|
||||||
var resendEmailButton = new ExtendedButton
|
var resendEmailButton = new ExtendedButton
|
||||||
{
|
{
|
||||||
Text = $"Enter the 6 digit verification code that was emailed to {redactedEmail}.",
|
Text = "Send verification code email again",
|
||||||
Style = (Style)Application.Current.Resources["btn-primaryAccent"],
|
Style = (Style)Application.Current.Resources["btn-primaryAccent"],
|
||||||
Margin = new Thickness(15, 0, 15, 25),
|
Margin = new Thickness(15, 0, 15, 0),
|
||||||
Command = new Command(() => SendEmail()),
|
Command = new Command(async () => await SendEmailAsync(true)),
|
||||||
Uppercase = false,
|
Uppercase = false,
|
||||||
BackgroundColor = Color.Transparent
|
BackgroundColor = Color.Transparent,
|
||||||
|
VerticalOptions = LayoutOptions.Start
|
||||||
};
|
};
|
||||||
|
|
||||||
layout.Children.Add(instruction);
|
|
||||||
layout.Children.Add(table);
|
|
||||||
layout.Children.Add(resendEmailButton);
|
layout.Children.Add(resendEmailButton);
|
||||||
layout.Children.Add(anotherMethodButton);
|
layout.Children.Add(anotherMethodButton);
|
||||||
break;
|
break;
|
||||||
|
@ -184,15 +188,30 @@ namespace Bit.App.Pages
|
||||||
{
|
{
|
||||||
Uri = $"http://192.168.1.6:4001/duo-mobile.html?host={host}&request={req}",
|
Uri = $"http://192.168.1.6:4001/duo-mobile.html?host={host}&request={req}",
|
||||||
HorizontalOptions = LayoutOptions.FillAndExpand,
|
HorizontalOptions = LayoutOptions.FillAndExpand,
|
||||||
VerticalOptions = LayoutOptions.FillAndExpand
|
VerticalOptions = LayoutOptions.FillAndExpand,
|
||||||
|
MinimumHeightRequest = 400
|
||||||
};
|
};
|
||||||
webView.RegisterAction(async (sig) =>
|
webView.RegisterAction(async (sig) =>
|
||||||
{
|
{
|
||||||
await LogInAsync(sig, false);
|
await LogInAsync(sig);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var table = new TwoFactorTable(
|
||||||
|
new TableSection(" ")
|
||||||
|
{
|
||||||
|
RememberCell
|
||||||
|
});
|
||||||
|
|
||||||
|
var layout = new StackLayout
|
||||||
|
{
|
||||||
|
Children = { webView, table, anotherMethodButton },
|
||||||
|
Spacing = 0
|
||||||
|
};
|
||||||
|
|
||||||
|
scrollView.Content = layout;
|
||||||
|
|
||||||
Title = "Duo";
|
Title = "Duo";
|
||||||
Content = webView;
|
Content = scrollView;
|
||||||
}
|
}
|
||||||
else if(_providerType == TwoFactorProviderType.YubiKey)
|
else if(_providerType == TwoFactorProviderType.YubiKey)
|
||||||
{
|
{
|
||||||
|
@ -254,28 +273,99 @@ namespace Bit.App.Pages
|
||||||
|
|
||||||
private async void AnotherMethodAsync()
|
private async void AnotherMethodAsync()
|
||||||
{
|
{
|
||||||
await Navigation.PushForDeviceAsync(new TwoFactorMethodsPage(_email, _result));
|
var beforeProviderType = _providerType;
|
||||||
|
|
||||||
|
var options = new List<string>();
|
||||||
|
if(_providers.ContainsKey(TwoFactorProviderType.Authenticator))
|
||||||
|
{
|
||||||
|
options.Add("Authenticator App");
|
||||||
|
}
|
||||||
|
|
||||||
|
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"];
|
||||||
|
if(_deviceInfoService.NfcEnabled || nfcKey)
|
||||||
|
{
|
||||||
|
options.Add("YubiKey NFC Security Key");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(_providers.ContainsKey(TwoFactorProviderType.Email))
|
||||||
|
{
|
||||||
|
options.Add("Email");
|
||||||
|
}
|
||||||
|
|
||||||
|
options.Add("Recovery Code");
|
||||||
|
|
||||||
|
var selection = await DisplayActionSheet("Two-step Login Options", AppResources.Cancel, null, options.ToArray());
|
||||||
|
if(selection == "Authenticator App")
|
||||||
|
{
|
||||||
|
_providerType = TwoFactorProviderType.Authenticator;
|
||||||
|
}
|
||||||
|
else if(selection == "Duo")
|
||||||
|
{
|
||||||
|
_providerType = TwoFactorProviderType.Duo;
|
||||||
|
}
|
||||||
|
else if(selection == "YubiKey NFC Security Key")
|
||||||
|
{
|
||||||
|
_providerType = TwoFactorProviderType.YubiKey;
|
||||||
|
}
|
||||||
|
else if(selection == "Email")
|
||||||
|
{
|
||||||
|
_providerType = TwoFactorProviderType.Email;
|
||||||
|
}
|
||||||
|
else if(selection == "Recovery Code")
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SendEmail()
|
private async Task SendEmailAsync(bool doToast)
|
||||||
{
|
{
|
||||||
|
if(_providerType != TwoFactorProviderType.Email)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
var response = await _twoFactorApiRepository.PostSendEmailLoginAsync(new Models.Api.TwoFactorEmailRequest
|
||||||
|
{
|
||||||
|
Email = _email,
|
||||||
|
MasterPasswordHash = _masterPasswordHash
|
||||||
|
});
|
||||||
|
|
||||||
private void Recover()
|
if(response.Succeeded && doToast)
|
||||||
{
|
{
|
||||||
Device.OpenUri(new Uri("https://help.bitwarden.com/article/lost-two-step-device/"));
|
_userDialogs.Toast("Verification email sent.");
|
||||||
|
}
|
||||||
|
else if(!response.Succeeded)
|
||||||
|
{
|
||||||
|
_userDialogs.Alert("Could not send verification email. Try again.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void Entry_Completed(object sender, EventArgs e)
|
private async void Entry_Completed(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
var token = TokenCell.Entry.Text.Trim().Replace(" ", "");
|
var token = TokenCell.Entry.Text.Trim().Replace(" ", "");
|
||||||
await LogInAsync(token, RememberCell.On);
|
await LogInAsync(token);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task LogInAsync(string token, bool remember)
|
private async Task LogInAsync(string token)
|
||||||
{
|
{
|
||||||
if(_lastAction.LastActionWasRecent())
|
if(!_providerType.HasValue || _lastAction.LastActionWasRecent())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -288,8 +378,8 @@ namespace Bit.App.Pages
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_userDialogs.ShowLoading(AppResources.ValidatingCode, MaskType.Black);
|
_userDialogs.ShowLoading(string.Concat(AppResources.Validating, "..."), MaskType.Black);
|
||||||
var response = await _authService.TokenPostTwoFactorAsync(_providerType.Value, token, remember,
|
var response = await _authService.TokenPostTwoFactorAsync(_providerType.Value, token, RememberCell.On,
|
||||||
_email, _masterPasswordHash, _key);
|
_email, _masterPasswordHash, _key);
|
||||||
_userDialogs.HideLoading();
|
_userDialogs.HideLoading();
|
||||||
if(!response.Success)
|
if(!response.Success)
|
||||||
|
@ -299,7 +389,7 @@ namespace Bit.App.Pages
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_googleAnalyticsService.TrackAppEvent("LoggedIn From Two-step");
|
_googleAnalyticsService.TrackAppEvent("LoggedIn From Two-step", _providerType.Value.ToString());
|
||||||
|
|
||||||
if(Device.RuntimePlatform == Device.Android)
|
if(Device.RuntimePlatform == Device.Android)
|
||||||
{
|
{
|
||||||
|
@ -320,11 +410,6 @@ namespace Bit.App.Pages
|
||||||
|
|
||||||
if(_providers != null)
|
if(_providers != null)
|
||||||
{
|
{
|
||||||
if(_providers.Count == 1)
|
|
||||||
{
|
|
||||||
return _providers.First().Key;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach(var p in _providers)
|
foreach(var p in _providers)
|
||||||
{
|
{
|
||||||
switch(p.Key)
|
switch(p.Key)
|
||||||
|
@ -348,7 +433,8 @@ namespace Bit.App.Pages
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case TwoFactorProviderType.YubiKey:
|
case TwoFactorProviderType.YubiKey:
|
||||||
if(!_deviceInfoService.NfcEnabled)
|
var nfcKey = p.Value.ContainsKey("Nfc") && (bool)p.Value["Nfc"];
|
||||||
|
if(!_deviceInfoService.NfcEnabled || !nfcKey)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -364,9 +450,9 @@ namespace Bit.App.Pages
|
||||||
return provider;
|
return provider;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ListenYubiKey(bool listen)
|
private void ListenYubiKey(bool listen, bool overrideCheck = false)
|
||||||
{
|
{
|
||||||
if(_providerType == TwoFactorProviderType.YubiKey)
|
if(_providerType == TwoFactorProviderType.YubiKey || overrideCheck)
|
||||||
{
|
{
|
||||||
MessagingCenter.Send(Application.Current, "ListenYubiKeyOTP", listen);
|
MessagingCenter.Send(Application.Current, "ListenYubiKeyOTP", listen);
|
||||||
}
|
}
|
||||||
|
@ -391,7 +477,7 @@ namespace Bit.App.Pages
|
||||||
MessagingCenter.Unsubscribe<Application, string>(Application.Current, "GotYubiKeyOTP");
|
MessagingCenter.Unsubscribe<Application, string>(Application.Current, "GotYubiKeyOTP");
|
||||||
if(_providerType == TwoFactorProviderType.YubiKey)
|
if(_providerType == TwoFactorProviderType.YubiKey)
|
||||||
{
|
{
|
||||||
await LogInAsync(otp, RememberCell.On);
|
await LogInAsync(otp);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,141 +0,0 @@
|
||||||
using System;
|
|
||||||
using Bit.App.Controls;
|
|
||||||
using Xamarin.Forms;
|
|
||||||
using Bit.App.Models;
|
|
||||||
|
|
||||||
namespace Bit.App.Pages
|
|
||||||
{
|
|
||||||
public class TwoFactorMethodsPage : ExtendedContentPage
|
|
||||||
{
|
|
||||||
private readonly string _email;
|
|
||||||
private readonly FullLoginResult _result;
|
|
||||||
|
|
||||||
public TwoFactorMethodsPage(string email, FullLoginResult result)
|
|
||||||
: base(updateActivity: false)
|
|
||||||
{
|
|
||||||
_email = email;
|
|
||||||
_result = result;
|
|
||||||
|
|
||||||
Init();
|
|
||||||
}
|
|
||||||
|
|
||||||
public ExtendedTextCell AuthenticatorCell { get; set; }
|
|
||||||
public ExtendedTextCell EmailCell { get; set; }
|
|
||||||
public ExtendedTextCell DuoCell { get; set; }
|
|
||||||
public ExtendedTextCell RecoveryCell { get; set; }
|
|
||||||
|
|
||||||
private void Init()
|
|
||||||
{
|
|
||||||
var section = new TableSection(" ");
|
|
||||||
|
|
||||||
if(_result.TwoFactorProviders.ContainsKey(Enums.TwoFactorProviderType.Authenticator))
|
|
||||||
{
|
|
||||||
AuthenticatorCell = new ExtendedTextCell
|
|
||||||
{
|
|
||||||
Text = "Authenticator App",
|
|
||||||
Detail = "Use an authenticator app (such as Authy or Google Authenticator) to generate time-based verification codes."
|
|
||||||
};
|
|
||||||
section.Add(AuthenticatorCell);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(_result.TwoFactorProviders.ContainsKey(Enums.TwoFactorProviderType.Duo))
|
|
||||||
{
|
|
||||||
DuoCell = new ExtendedTextCell
|
|
||||||
{
|
|
||||||
Text = "Duo",
|
|
||||||
Detail = "Use duo."
|
|
||||||
};
|
|
||||||
section.Add(DuoCell);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(_result.TwoFactorProviders.ContainsKey(Enums.TwoFactorProviderType.Email))
|
|
||||||
{
|
|
||||||
EmailCell = new ExtendedTextCell
|
|
||||||
{
|
|
||||||
Text = "Email",
|
|
||||||
Detail = "Verification codes will be emailed to you."
|
|
||||||
};
|
|
||||||
section.Add(EmailCell);
|
|
||||||
}
|
|
||||||
|
|
||||||
RecoveryCell = new ExtendedTextCell
|
|
||||||
{
|
|
||||||
Text = "Recovery Code",
|
|
||||||
Detail = "Lost access to all of your two-factor providers? Use your recovery code to disable all two-factor providers from your account."
|
|
||||||
};
|
|
||||||
section.Add(RecoveryCell);
|
|
||||||
|
|
||||||
var table = new ExtendedTableView
|
|
||||||
{
|
|
||||||
EnableScrolling = true,
|
|
||||||
Intent = TableIntent.Settings,
|
|
||||||
HasUnevenRows = true,
|
|
||||||
Root = new TableRoot
|
|
||||||
{
|
|
||||||
section
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if(Device.RuntimePlatform == Device.iOS)
|
|
||||||
{
|
|
||||||
table.RowHeight = -1;
|
|
||||||
table.EstimatedRowHeight = 100;
|
|
||||||
}
|
|
||||||
|
|
||||||
Title = "Two-step Login Options";
|
|
||||||
Content = table;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnAppearing()
|
|
||||||
{
|
|
||||||
base.OnAppearing();
|
|
||||||
if(AuthenticatorCell != null)
|
|
||||||
{
|
|
||||||
AuthenticatorCell.Tapped += AuthenticatorCell_Tapped;
|
|
||||||
}
|
|
||||||
if(DuoCell != null)
|
|
||||||
{
|
|
||||||
DuoCell.Tapped += DuoCell_Tapped;
|
|
||||||
}
|
|
||||||
if(EmailCell != null)
|
|
||||||
{
|
|
||||||
EmailCell.Tapped += EmailCell_Tapped;
|
|
||||||
}
|
|
||||||
RecoveryCell.Tapped += RecoveryCell_Tapped;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnDisappearing()
|
|
||||||
{
|
|
||||||
base.OnDisappearing();
|
|
||||||
if(AuthenticatorCell != null)
|
|
||||||
{
|
|
||||||
AuthenticatorCell.Tapped -= AuthenticatorCell_Tapped;
|
|
||||||
}
|
|
||||||
if(DuoCell != null)
|
|
||||||
{
|
|
||||||
DuoCell.Tapped -= DuoCell_Tapped;
|
|
||||||
}
|
|
||||||
if(EmailCell != null)
|
|
||||||
{
|
|
||||||
EmailCell.Tapped -= EmailCell_Tapped;
|
|
||||||
}
|
|
||||||
RecoveryCell.Tapped -= RecoveryCell_Tapped;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void AuthenticatorCell_Tapped(object sender, EventArgs e)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RecoveryCell_Tapped(object sender, EventArgs e)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
private void EmailCell_Tapped(object sender, EventArgs e)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DuoCell_Tapped(object sender, EventArgs e)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
53
src/App/Repositories/TwoFactorApiRepository.cs
Normal file
53
src/App/Repositories/TwoFactorApiRepository.cs
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
using System;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Bit.App.Abstractions;
|
||||||
|
using Bit.App.Models.Api;
|
||||||
|
using Plugin.Connectivity.Abstractions;
|
||||||
|
|
||||||
|
namespace Bit.App.Repositories
|
||||||
|
{
|
||||||
|
public class TwoFactorApiRepository : BaseApiRepository, ITwoFactorApiRepository
|
||||||
|
{
|
||||||
|
public TwoFactorApiRepository(
|
||||||
|
IConnectivity connectivity,
|
||||||
|
IHttpService httpService,
|
||||||
|
ITokenService tokenService)
|
||||||
|
: base(connectivity, httpService, tokenService)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
protected override string ApiRoute => "two-factor";
|
||||||
|
|
||||||
|
public virtual async Task<ApiResult> PostSendEmailLoginAsync(TwoFactorEmailRequest requestObj)
|
||||||
|
{
|
||||||
|
if(!Connectivity.IsConnected)
|
||||||
|
{
|
||||||
|
return HandledNotConnected();
|
||||||
|
}
|
||||||
|
|
||||||
|
using(var client = HttpService.ApiClient)
|
||||||
|
{
|
||||||
|
var requestMessage = new TokenHttpRequestMessage(requestObj)
|
||||||
|
{
|
||||||
|
Method = HttpMethod.Post,
|
||||||
|
RequestUri = new Uri(client.BaseAddress, string.Concat(ApiRoute, "/send-email-login")),
|
||||||
|
};
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var response = await client.SendAsync(requestMessage).ConfigureAwait(false);
|
||||||
|
if(!response.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
return await HandleErrorAsync(response).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ApiResult.Success(response.StatusCode);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return HandledWebException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
6
src/App/Resources/AppResources.Designer.cs
generated
6
src/App/Resources/AppResources.Designer.cs
generated
|
@ -1943,11 +1943,11 @@ namespace Bit.App.Resources {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Validating code....
|
/// Looks up a localized string similar to Validating.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string ValidatingCode {
|
public static string Validating {
|
||||||
get {
|
get {
|
||||||
return ResourceManager.GetString("ValidatingCode", resourceCulture);
|
return ResourceManager.GetString("Validating", resourceCulture);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -728,8 +728,8 @@
|
||||||
<data name="UnlockWithPIN" xml:space="preserve">
|
<data name="UnlockWithPIN" xml:space="preserve">
|
||||||
<value>Unlock with PIN Code</value>
|
<value>Unlock with PIN Code</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ValidatingCode" xml:space="preserve">
|
<data name="Validating" xml:space="preserve">
|
||||||
<value>Validating code...</value>
|
<value>Validating</value>
|
||||||
<comment>Message shown when interacting with the server</comment>
|
<comment>Message shown when interacting with the server</comment>
|
||||||
</data>
|
</data>
|
||||||
<data name="VerificationCode" xml:space="preserve">
|
<data name="VerificationCode" xml:space="preserve">
|
||||||
|
|
|
@ -280,6 +280,7 @@ namespace Bit.iOS
|
||||||
container.RegisterSingleton<ICipherApiRepository, CipherApiRepository>();
|
container.RegisterSingleton<ICipherApiRepository, CipherApiRepository>();
|
||||||
container.RegisterSingleton<ISettingsRepository, SettingsRepository>();
|
container.RegisterSingleton<ISettingsRepository, SettingsRepository>();
|
||||||
container.RegisterSingleton<ISettingsApiRepository, SettingsApiRepository>();
|
container.RegisterSingleton<ISettingsApiRepository, SettingsApiRepository>();
|
||||||
|
container.RegisterSingleton<ITwoFactorApiRepository, TwoFactorApiRepository>();
|
||||||
|
|
||||||
// Other
|
// Other
|
||||||
container.RegisterSingleton(CrossConnectivity.Current);
|
container.RegisterSingleton(CrossConnectivity.Current);
|
||||||
|
|
Loading…
Reference in a new issue