two-factor page UI

This commit is contained in:
Kyle Spearrin 2019-05-27 11:57:10 -04:00
parent e8705d49f2
commit bf4f0bdba0
4 changed files with 131 additions and 9 deletions

View file

@ -100,7 +100,8 @@ namespace Bit.App.Pages
}
if(response.TwoFactor)
{
// TODO: 2fa page
var page = new TwoFactorPage();
await Page.Navigation.PushModalAsync(new NavigationPage(page));
}
else
{

View file

@ -16,15 +16,103 @@
<ContentPage.Resources>
<ResourceDictionary>
<u:InverseBoolConverter x:Key="inverseBool" />
<u:IsNullConverter x:Key="isNull" />
</ResourceDictionary>
</ContentPage.Resources>
<ContentPage.ToolbarItems>
<ToolbarItem Text="{u:I18n Continue}" Clicked="Continue_Clicked" />
<ToolbarItem Text="{u:I18n Continue}" Clicked="Continue_Clicked" x:Name="_continueItem" />
</ContentPage.ToolbarItems>
<ScrollView>
<StackLayout Spacing="0" Padding="0">
<StackLayout Spacing="0" Padding="0" IsVisible="{Binding TotpMethod, Mode=OneWay}">
<Label
Text="{Binding TotpInstruction, Mode=OneWay}"
Margin="20, 0"
HorizontalTextAlignment="Center" />
<StackLayout StyleClass="box">
<StackLayout StyleClass="box-row, box-row-input">
<Label
Text="{u:I18n VerificationCode}"
StyleClass="box-label" />
<Entry
Text="{Binding Token}"
Keyboard="Numeric"
StyleClass="box-value" />
</StackLayout>
<StackLayout StyleClass="box-row, box-row-switch">
<Label
Text="{u:I18n RememberMe}"
StyleClass="box-label, box-label-regular"
HorizontalOptions="StartAndExpand" />
<Switch
IsToggled="{Binding Remember}"
StyleClass="box-value"
HorizontalOptions="End" />
</StackLayout>
</StackLayout>
</StackLayout>
<StackLayout Spacing="0" Padding="0" IsVisible="{Binding YubikeyMethod, Mode=OneWay}">
<Label
Text="{Binding YubikeyInstruction, Mode=OneWay}"
Margin="20, 0"
HorizontalTextAlignment="Center" />
<Image
Source="yubikey.png"
Margin="20, 0"
WidthRequest="266"
HeightRequest="160"
HorizontalOptions="Center" />
<StackLayout StyleClass="box">
<StackLayout StyleClass="box-row, box-row-input">
<Entry
Text="{Binding Token}"
StyleClass="box-value" />
</StackLayout>
<StackLayout StyleClass="box-row, box-row-switch">
<Label
Text="{u:I18n RememberMe}"
StyleClass="box-label, box-label-regular"
HorizontalOptions="StartAndExpand" />
<Switch
IsToggled="{Binding Remember}"
StyleClass="box-value"
HorizontalOptions="End" />
</StackLayout>
</StackLayout>
</StackLayout>
<StackLayout Spacing="0" Padding="0" IsVisible="{Binding DuoMethod, Mode=OneWay}">
<controls:HybridWebView
x:Name="_duoWebView"
HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand"
MinimumHeightRequest="400" />
<StackLayout StyleClass="box">
<StackLayout StyleClass="box-row, box-row-switch">
<Label
Text="{u:I18n RememberMe}"
StyleClass="box-label, box-label-regular"
HorizontalOptions="StartAndExpand" />
<Switch
IsToggled="{Binding Remember}"
StyleClass="box-value"
HorizontalOptions="End" />
</StackLayout>
</StackLayout>
</StackLayout>
<StackLayout
Spacing="0"
Padding="0"
IsVisible="{Binding SelectedProviderType, Mode=OneWay, Converter={StaticResource isNull}}">
<Label
Text="{u:I18n NoTwoStepAvailable}"
Margin="20, 0"
HorizontalTextAlignment="Center" />
</StackLayout>
<Button Text="{u:I18n UseAnotherTwoStepMethod}"
Clicked="Methods_Clicked"></Button>
</StackLayout>
</ScrollView>
</pages:BaseContentPage>

View file

@ -1,4 +1,5 @@
using System;
using Bit.App.Controls;
using System;
namespace Bit.App.Pages
{
@ -11,8 +12,11 @@ namespace Bit.App.Pages
InitializeComponent();
_vm = BindingContext as TwoFactorPageViewModel;
_vm.Page = this;
DuoWebView = _duoWebView;
}
public HybridWebView DuoWebView { get; set; }
protected override void OnAppearing()
{
base.OnAppearing();

View file

@ -6,6 +6,7 @@ using Bit.Core.Exceptions;
using Bit.Core.Models.Request;
using Bit.Core.Utilities;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using Xamarin.Forms;
@ -19,10 +20,12 @@ namespace Bit.App.Pages
private readonly IStorageService _storageService;
private readonly IApiService _apiService;
private readonly IPlatformUtilsService _platformUtilsService;
private readonly IEnvironmentService _environmentService;
private bool _u2fSupported = false;
private TwoFactorProviderType? _selectedProviderType;
private string _twoFactorEmail;
private string _webVaultUrl = "https://vault.bitwarden.com";
public TwoFactorPageViewModel()
{
@ -32,6 +35,7 @@ namespace Bit.App.Pages
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
_apiService = ServiceContainer.Resolve<IApiService>("apiService");
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
_environmentService = ServiceContainer.Resolve<IEnvironmentService>("environmentService");
}
public string TwoFactorEmail
@ -52,6 +56,14 @@ namespace Bit.App.Pages
public bool EmailMethod => SelectedProviderType == TwoFactorProviderType.Email;
public bool TotpMethod => AuthenticatorMethod || EmailMethod;
public string TotpInstruction => AuthenticatorMethod ? AppResources.EnterVerificationCodeApp :
AppResources.EnterVerificationCodeEmail;
public string YubikeyInstruction => Device.RuntimePlatform == Device.iOS ? AppResources.YubiKeyInstructionIos :
AppResources.YubiKeyInstruction;
public TwoFactorProviderType? SelectedProviderType
{
get => _selectedProviderType;
@ -60,7 +72,9 @@ namespace Bit.App.Pages
nameof(EmailMethod),
nameof(DuoMethod),
nameof(YubikeyMethod),
nameof(AuthenticatorMethod)
nameof(AuthenticatorMethod),
nameof(TotpMethod),
nameof(TotpInstruction)
});
}
@ -74,10 +88,19 @@ namespace Bit.App.Pages
return;
}
if(!string.IsNullOrWhiteSpace(_environmentService.BaseUrl))
{
_webVaultUrl = _environmentService.BaseUrl;
}
else if(!string.IsNullOrWhiteSpace(_environmentService.WebVaultUrl))
{
_webVaultUrl = _environmentService.WebVaultUrl;
}
// TODO: init U2F
_u2fSupported = false;
var selectedProviderType = _authService.GetDefaultTwoFactorProvider(_u2fSupported);
SelectedProviderType = _authService.GetDefaultTwoFactorProvider(_u2fSupported);
Load();
}
@ -97,9 +120,15 @@ namespace Bit.App.Pages
break;
case TwoFactorProviderType.Duo:
case TwoFactorProviderType.OrganizationDuo:
// TODO: init duo
var host = providerData["Host"] as string;
var signature = providerData["Signature"] as string;
var host = WebUtility.UrlEncode(providerData["Host"] as string);
var req = WebUtility.UrlEncode(providerData["Signature"] as string);
var page = Page as TwoFactorPage;
page.DuoWebView.Uri = $"{_webVaultUrl}/duo-connector.html?host={host}&request={req}";
page.DuoWebView.RegisterAction(async sig =>
{
Token = sig;
await SubmitAsync();
});
break;
case TwoFactorProviderType.Email:
TwoFactorEmail = providerData["Email"] as string;