mirror of
https://github.com/bitwarden/mobile.git
synced 2024-12-17 15:27:43 +01:00
register page
This commit is contained in:
parent
15cda95c64
commit
9965121011
@ -25,6 +25,9 @@
|
|||||||
<Compile Update="Pages\Accounts\HintPage.xaml.cs">
|
<Compile Update="Pages\Accounts\HintPage.xaml.cs">
|
||||||
<DependentUpon>HintPage.xaml</DependentUpon>
|
<DependentUpon>HintPage.xaml</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Update="Pages\Accounts\RegisterPage.xaml.cs">
|
||||||
|
<DependentUpon>RegisterPage.xaml</DependentUpon>
|
||||||
|
</Compile>
|
||||||
<Compile Update="Pages\Accounts\LoginPage.xaml.cs">
|
<Compile Update="Pages\Accounts\LoginPage.xaml.cs">
|
||||||
<DependentUpon>LoginPage.xaml</DependentUpon>
|
<DependentUpon>LoginPage.xaml</DependentUpon>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
117
src/App/Pages/Accounts/RegisterPage.xaml
Normal file
117
src/App/Pages/Accounts/RegisterPage.xaml
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<pages:BaseContentPage
|
||||||
|
xmlns="http://xamarin.com/schemas/2014/forms"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||||
|
x:Class="Bit.App.Pages.RegisterPage"
|
||||||
|
xmlns:pages="clr-namespace:Bit.App.Pages"
|
||||||
|
xmlns:controls="clr-namespace:Bit.App.Controls"
|
||||||
|
xmlns:u="clr-namespace:Bit.App.Utilities"
|
||||||
|
x:DataType="pages:RegisterPageViewModel"
|
||||||
|
Title="{Binding PageTitle}">
|
||||||
|
|
||||||
|
<ContentPage.BindingContext>
|
||||||
|
<pages:RegisterPageViewModel />
|
||||||
|
</ContentPage.BindingContext>
|
||||||
|
|
||||||
|
<ContentPage.Resources>
|
||||||
|
<ResourceDictionary>
|
||||||
|
<u:InverseBoolConverter x:Key="inverseBool" />
|
||||||
|
</ResourceDictionary>
|
||||||
|
</ContentPage.Resources>
|
||||||
|
|
||||||
|
<ContentPage.ToolbarItems>
|
||||||
|
<ToolbarItem Text="{u:I18n Submit}" Clicked="Submit_Clicked" />
|
||||||
|
</ContentPage.ToolbarItems>
|
||||||
|
|
||||||
|
<ScrollView>
|
||||||
|
<StackLayout Spacing="20">
|
||||||
|
<StackLayout StyleClass="box">
|
||||||
|
<StackLayout StyleClass="box-row">
|
||||||
|
<Label
|
||||||
|
Text="{u:I18n EmailAddress}"
|
||||||
|
StyleClass="box-label" />
|
||||||
|
<Entry
|
||||||
|
x:Name="_email"
|
||||||
|
Text="{Binding Email}"
|
||||||
|
Keyboard="Email"
|
||||||
|
StyleClass="box-value" />
|
||||||
|
</StackLayout>
|
||||||
|
<Grid StyleClass="box-row">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<Label
|
||||||
|
Text="{u:I18n MasterPassword}"
|
||||||
|
StyleClass="box-label"
|
||||||
|
Grid.Row="0"
|
||||||
|
Grid.Column="0" />
|
||||||
|
<controls:MonoEntry
|
||||||
|
x:Name="_masterPassword"
|
||||||
|
Text="{Binding MasterPassword}"
|
||||||
|
StyleClass="box-value"
|
||||||
|
IsPassword="{Binding ShowPassword, Converter={StaticResource inverseBool}}"
|
||||||
|
Grid.Row="1"
|
||||||
|
Grid.Column="0" />
|
||||||
|
<controls:FaButton
|
||||||
|
StyleClass="box-row-button, box-row-button-platform"
|
||||||
|
Text="{Binding ShowPasswordIcon}"
|
||||||
|
Command="{Binding TogglePasswordCommand}"
|
||||||
|
Grid.Row="0"
|
||||||
|
Grid.Column="1"
|
||||||
|
Grid.RowSpan="2" />
|
||||||
|
</Grid>
|
||||||
|
<Label
|
||||||
|
Text="{u:I18n MasterPasswordDescription}"
|
||||||
|
StyleClass="box-footer-label" />
|
||||||
|
</StackLayout>
|
||||||
|
<StackLayout StyleClass="box">
|
||||||
|
<Grid StyleClass="box-row">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto" />
|
||||||
|
<RowDefinition Height="*" />
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="*" />
|
||||||
|
<ColumnDefinition Width="Auto" />
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<Label
|
||||||
|
Text="{u:I18n RetypeMasterPassword}"
|
||||||
|
StyleClass="box-label"
|
||||||
|
Grid.Row="0"
|
||||||
|
Grid.Column="0" />
|
||||||
|
<controls:MonoEntry
|
||||||
|
x:Name="_confirmMasterPassword"
|
||||||
|
Text="{Binding ConfirmMasterPassword}"
|
||||||
|
StyleClass="box-value"
|
||||||
|
IsPassword="{Binding ShowPassword, Converter={StaticResource inverseBool}}"
|
||||||
|
Grid.Row="1"
|
||||||
|
Grid.Column="0" />
|
||||||
|
<controls:FaButton
|
||||||
|
StyleClass="box-row-button, box-row-button-platform"
|
||||||
|
Text="{Binding ShowPasswordIcon}"
|
||||||
|
Command="{Binding ToggleConfirmPasswordCommand}"
|
||||||
|
Grid.Row="0"
|
||||||
|
Grid.Column="1"
|
||||||
|
Grid.RowSpan="2" />
|
||||||
|
</Grid>
|
||||||
|
<StackLayout StyleClass="box-row">
|
||||||
|
<Label
|
||||||
|
Text="{u:I18n MasterPasswordHint}"
|
||||||
|
StyleClass="box-label" />
|
||||||
|
<Entry
|
||||||
|
Text="{Binding Hint}"
|
||||||
|
StyleClass="box-value" />
|
||||||
|
</StackLayout>
|
||||||
|
<Label
|
||||||
|
Text="{u:I18n MasterPasswordHintDescription}"
|
||||||
|
StyleClass="box-footer-label" />
|
||||||
|
</StackLayout>
|
||||||
|
</StackLayout>
|
||||||
|
</ScrollView>
|
||||||
|
|
||||||
|
</pages:BaseContentPage>
|
33
src/App/Pages/Accounts/RegisterPage.xaml.cs
Normal file
33
src/App/Pages/Accounts/RegisterPage.xaml.cs
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
using System;
|
||||||
|
using Xamarin.Forms;
|
||||||
|
|
||||||
|
namespace Bit.App.Pages
|
||||||
|
{
|
||||||
|
public partial class RegisterPage : BaseContentPage
|
||||||
|
{
|
||||||
|
private RegisterPageViewModel _vm;
|
||||||
|
|
||||||
|
public RegisterPage()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
_vm = BindingContext as RegisterPageViewModel;
|
||||||
|
_vm.Page = this;
|
||||||
|
MasterPasswordEntry = _masterPassword;
|
||||||
|
ConfirmMasterPasswordEntry = _confirmMasterPassword;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Entry MasterPasswordEntry { get; set; }
|
||||||
|
public Entry ConfirmMasterPasswordEntry { get; set; }
|
||||||
|
|
||||||
|
protected override async void OnAppearing()
|
||||||
|
{
|
||||||
|
base.OnAppearing();
|
||||||
|
RequestFocus(_email);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void Submit_Clicked(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
await _vm.SubmitAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
138
src/App/Pages/Accounts/RegisterPageViewModel.cs
Normal file
138
src/App/Pages/Accounts/RegisterPageViewModel.cs
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
using Bit.App.Abstractions;
|
||||||
|
using Bit.App.Resources;
|
||||||
|
using Bit.Core.Abstractions;
|
||||||
|
using Bit.Core.Enums;
|
||||||
|
using Bit.Core.Exceptions;
|
||||||
|
using Bit.Core.Models.Request;
|
||||||
|
using Bit.Core.Utilities;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Xamarin.Forms;
|
||||||
|
|
||||||
|
namespace Bit.App.Pages
|
||||||
|
{
|
||||||
|
public class RegisterPageViewModel : BaseViewModel
|
||||||
|
{
|
||||||
|
private readonly IDeviceActionService _deviceActionService;
|
||||||
|
private readonly IApiService _apiService;
|
||||||
|
private readonly ICryptoService _cryptoService;
|
||||||
|
|
||||||
|
private bool _showPassword;
|
||||||
|
|
||||||
|
public RegisterPageViewModel()
|
||||||
|
{
|
||||||
|
_deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
||||||
|
_apiService = ServiceContainer.Resolve<IApiService>("apiService");
|
||||||
|
_cryptoService = ServiceContainer.Resolve<ICryptoService>("cryptoService");
|
||||||
|
|
||||||
|
PageTitle = AppResources.Bitwarden;
|
||||||
|
TogglePasswordCommand = new Command(TogglePassword);
|
||||||
|
ToggleConfirmPasswordCommand = new Command(ToggleConfirmPassword);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ShowPassword
|
||||||
|
{
|
||||||
|
get => _showPassword;
|
||||||
|
set => SetProperty(ref _showPassword, value,
|
||||||
|
additionalPropertyNames: new string[]
|
||||||
|
{
|
||||||
|
nameof(ShowPasswordIcon)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public Command TogglePasswordCommand { get; }
|
||||||
|
public Command ToggleConfirmPasswordCommand { get; }
|
||||||
|
public string ShowPasswordIcon => ShowPassword ? "" : "";
|
||||||
|
public string Name { get; set; }
|
||||||
|
public string Email { get; set; }
|
||||||
|
public string MasterPassword { get; set; }
|
||||||
|
public string ConfirmMasterPassword { get; set; }
|
||||||
|
public string Hint { get; set; }
|
||||||
|
|
||||||
|
public async Task SubmitAsync()
|
||||||
|
{
|
||||||
|
if(string.IsNullOrWhiteSpace(Email))
|
||||||
|
{
|
||||||
|
await Page.DisplayAlert(AppResources.AnErrorHasOccurred,
|
||||||
|
string.Format(AppResources.ValidationFieldRequired, AppResources.EmailAddress),
|
||||||
|
AppResources.Ok);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(!Email.Contains("@"))
|
||||||
|
{
|
||||||
|
await Page.DisplayAlert(AppResources.AnErrorHasOccurred, AppResources.InvalidEmail, AppResources.Ok);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(string.IsNullOrWhiteSpace(MasterPassword))
|
||||||
|
{
|
||||||
|
await Page.DisplayAlert(AppResources.AnErrorHasOccurred,
|
||||||
|
string.Format(AppResources.ValidationFieldRequired, AppResources.MasterPassword),
|
||||||
|
AppResources.Ok);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(MasterPassword.Length < 8)
|
||||||
|
{
|
||||||
|
await Page.DisplayAlert(AppResources.AnErrorHasOccurred,
|
||||||
|
AppResources.MasterPasswordLengthValMessage, AppResources.Ok);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(MasterPassword != ConfirmMasterPassword)
|
||||||
|
{
|
||||||
|
await Page.DisplayAlert(AppResources.AnErrorHasOccurred,
|
||||||
|
AppResources.MasterPasswordConfirmationValMessage, AppResources.Ok);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Password strength check?
|
||||||
|
|
||||||
|
Name = string.IsNullOrWhiteSpace(Name) ? null : Name;
|
||||||
|
Email = Email.Trim().ToLower();
|
||||||
|
var kdf = KdfType.PBKDF2_SHA256;
|
||||||
|
var kdfIterations = 100_000;
|
||||||
|
var key = await _cryptoService.MakeKeyAsync(MasterPassword, Email, kdf, kdfIterations);
|
||||||
|
var encKey = await _cryptoService.MakeEncKeyAsync(key);
|
||||||
|
var hashedPassword = await _cryptoService.HashPasswordAsync(MasterPassword, key);
|
||||||
|
var keys = await _cryptoService.MakeKeyPairAsync(encKey.Item1);
|
||||||
|
var request = new RegisterRequest
|
||||||
|
{
|
||||||
|
Email = Email,
|
||||||
|
Name = Name,
|
||||||
|
MasterPasswordHash = hashedPassword,
|
||||||
|
MasterPasswordHint = Hint,
|
||||||
|
Key = encKey.Item2.EncryptedString,
|
||||||
|
Kdf = kdf,
|
||||||
|
KdfIterations = kdfIterations,
|
||||||
|
Keys = new KeysRequest
|
||||||
|
{
|
||||||
|
PublicKey = keys.Item1,
|
||||||
|
EncryptedPrivateKey = keys.Item2.EncryptedString
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// TODO: org invite?
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await _deviceActionService.ShowLoadingAsync(AppResources.LoggingIn);
|
||||||
|
await _apiService.PostRegisterAsync(request);
|
||||||
|
await _deviceActionService.HideLoadingAsync();
|
||||||
|
// TODO: dismiss this page from home page and pass email for login page
|
||||||
|
}
|
||||||
|
catch(ApiException e)
|
||||||
|
{
|
||||||
|
await _deviceActionService.HideLoadingAsync();
|
||||||
|
await Page.DisplayAlert(AppResources.AnErrorHasOccurred, e.Error.GetSingleMessage(), AppResources.Ok);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void TogglePassword()
|
||||||
|
{
|
||||||
|
ShowPassword = !ShowPassword;
|
||||||
|
(Page as RegisterPage).MasterPasswordEntry.Focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ToggleConfirmPassword()
|
||||||
|
{
|
||||||
|
ShowPassword = !ShowPassword;
|
||||||
|
(Page as RegisterPage).ConfirmMasterPasswordEntry.Focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -24,7 +24,8 @@
|
|||||||
HorizontalTextAlignment="Center"></Label>
|
HorizontalTextAlignment="Center"></Label>
|
||||||
<Button Text="{u:I18n LogIn}"
|
<Button Text="{u:I18n LogIn}"
|
||||||
Clicked="LogIn_Clicked"></Button>
|
Clicked="LogIn_Clicked"></Button>
|
||||||
<Button Text="{u:I18n CreateAccount}"></Button>
|
<Button Text="{u:I18n CreateAccount}"
|
||||||
|
Clicked="Register_Clicked"></Button>
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
</StackLayout>
|
</StackLayout>
|
||||||
|
|
||||||
|
@ -19,5 +19,10 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
Navigation.PushModalAsync(new NavigationPage(new LoginPage()));
|
Navigation.PushModalAsync(new NavigationPage(new LoginPage()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void Register_Clicked(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
Navigation.PushModalAsync(new NavigationPage(new RegisterPage()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace Bit.App.Pages
|
namespace Bit.App.Pages
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user