mirror of
https://github.com/bitwarden/android.git
synced 2024-12-25 02:18:27 +03:00
two-factor page UI
This commit is contained in:
parent
e8705d49f2
commit
bf4f0bdba0
4 changed files with 131 additions and 9 deletions
|
@ -100,7 +100,8 @@ namespace Bit.App.Pages
|
||||||
}
|
}
|
||||||
if(response.TwoFactor)
|
if(response.TwoFactor)
|
||||||
{
|
{
|
||||||
// TODO: 2fa page
|
var page = new TwoFactorPage();
|
||||||
|
await Page.Navigation.PushModalAsync(new NavigationPage(page));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -16,15 +16,103 @@
|
||||||
<ContentPage.Resources>
|
<ContentPage.Resources>
|
||||||
<ResourceDictionary>
|
<ResourceDictionary>
|
||||||
<u:InverseBoolConverter x:Key="inverseBool" />
|
<u:InverseBoolConverter x:Key="inverseBool" />
|
||||||
|
<u:IsNullConverter x:Key="isNull" />
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
</ContentPage.Resources>
|
</ContentPage.Resources>
|
||||||
|
|
||||||
<ContentPage.ToolbarItems>
|
<ContentPage.ToolbarItems>
|
||||||
<ToolbarItem Text="{u:I18n Continue}" Clicked="Continue_Clicked" />
|
<ToolbarItem Text="{u:I18n Continue}" Clicked="Continue_Clicked" x:Name="_continueItem" />
|
||||||
</ContentPage.ToolbarItems>
|
</ContentPage.ToolbarItems>
|
||||||
|
|
||||||
<ScrollView>
|
<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>
|
</ScrollView>
|
||||||
|
|
||||||
</pages:BaseContentPage>
|
</pages:BaseContentPage>
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using System;
|
using Bit.App.Controls;
|
||||||
|
using System;
|
||||||
|
|
||||||
namespace Bit.App.Pages
|
namespace Bit.App.Pages
|
||||||
{
|
{
|
||||||
|
@ -11,8 +12,11 @@ namespace Bit.App.Pages
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
_vm = BindingContext as TwoFactorPageViewModel;
|
_vm = BindingContext as TwoFactorPageViewModel;
|
||||||
_vm.Page = this;
|
_vm.Page = this;
|
||||||
|
DuoWebView = _duoWebView;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public HybridWebView DuoWebView { get; set; }
|
||||||
|
|
||||||
protected override void OnAppearing()
|
protected override void OnAppearing()
|
||||||
{
|
{
|
||||||
base.OnAppearing();
|
base.OnAppearing();
|
||||||
|
|
|
@ -6,6 +6,7 @@ using Bit.Core.Exceptions;
|
||||||
using Bit.Core.Models.Request;
|
using Bit.Core.Models.Request;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
|
||||||
|
@ -19,10 +20,12 @@ namespace Bit.App.Pages
|
||||||
private readonly IStorageService _storageService;
|
private readonly IStorageService _storageService;
|
||||||
private readonly IApiService _apiService;
|
private readonly IApiService _apiService;
|
||||||
private readonly IPlatformUtilsService _platformUtilsService;
|
private readonly IPlatformUtilsService _platformUtilsService;
|
||||||
|
private readonly IEnvironmentService _environmentService;
|
||||||
|
|
||||||
private bool _u2fSupported = false;
|
private bool _u2fSupported = false;
|
||||||
private TwoFactorProviderType? _selectedProviderType;
|
private TwoFactorProviderType? _selectedProviderType;
|
||||||
private string _twoFactorEmail;
|
private string _twoFactorEmail;
|
||||||
|
private string _webVaultUrl = "https://vault.bitwarden.com";
|
||||||
|
|
||||||
public TwoFactorPageViewModel()
|
public TwoFactorPageViewModel()
|
||||||
{
|
{
|
||||||
|
@ -32,6 +35,7 @@ namespace Bit.App.Pages
|
||||||
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
_storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||||
_apiService = ServiceContainer.Resolve<IApiService>("apiService");
|
_apiService = ServiceContainer.Resolve<IApiService>("apiService");
|
||||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||||
|
_environmentService = ServiceContainer.Resolve<IEnvironmentService>("environmentService");
|
||||||
}
|
}
|
||||||
|
|
||||||
public string TwoFactorEmail
|
public string TwoFactorEmail
|
||||||
|
@ -52,6 +56,14 @@ namespace Bit.App.Pages
|
||||||
|
|
||||||
public bool EmailMethod => SelectedProviderType == TwoFactorProviderType.Email;
|
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
|
public TwoFactorProviderType? SelectedProviderType
|
||||||
{
|
{
|
||||||
get => _selectedProviderType;
|
get => _selectedProviderType;
|
||||||
|
@ -60,7 +72,9 @@ namespace Bit.App.Pages
|
||||||
nameof(EmailMethod),
|
nameof(EmailMethod),
|
||||||
nameof(DuoMethod),
|
nameof(DuoMethod),
|
||||||
nameof(YubikeyMethod),
|
nameof(YubikeyMethod),
|
||||||
nameof(AuthenticatorMethod)
|
nameof(AuthenticatorMethod),
|
||||||
|
nameof(TotpMethod),
|
||||||
|
nameof(TotpInstruction)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,10 +88,19 @@ namespace Bit.App.Pages
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(!string.IsNullOrWhiteSpace(_environmentService.BaseUrl))
|
||||||
|
{
|
||||||
|
_webVaultUrl = _environmentService.BaseUrl;
|
||||||
|
}
|
||||||
|
else if(!string.IsNullOrWhiteSpace(_environmentService.WebVaultUrl))
|
||||||
|
{
|
||||||
|
_webVaultUrl = _environmentService.WebVaultUrl;
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: init U2F
|
// TODO: init U2F
|
||||||
_u2fSupported = false;
|
_u2fSupported = false;
|
||||||
|
|
||||||
var selectedProviderType = _authService.GetDefaultTwoFactorProvider(_u2fSupported);
|
SelectedProviderType = _authService.GetDefaultTwoFactorProvider(_u2fSupported);
|
||||||
Load();
|
Load();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,9 +120,15 @@ namespace Bit.App.Pages
|
||||||
break;
|
break;
|
||||||
case TwoFactorProviderType.Duo:
|
case TwoFactorProviderType.Duo:
|
||||||
case TwoFactorProviderType.OrganizationDuo:
|
case TwoFactorProviderType.OrganizationDuo:
|
||||||
// TODO: init duo
|
var host = WebUtility.UrlEncode(providerData["Host"] as string);
|
||||||
var host = providerData["Host"] as string;
|
var req = WebUtility.UrlEncode(providerData["Signature"] as string);
|
||||||
var signature = 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;
|
break;
|
||||||
case TwoFactorProviderType.Email:
|
case TwoFactorProviderType.Email:
|
||||||
TwoFactorEmail = providerData["Email"] as string;
|
TwoFactorEmail = providerData["Email"] as string;
|
||||||
|
|
Loading…
Reference in a new issue