mirror of
https://github.com/bitwarden/mobile
synced 2025-12-16 08:13:20 +00:00
Compare commits
4 Commits
ps/sdk
...
feature/pa
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7b237b4efe | ||
|
|
90fe9f8600 | ||
|
|
4e0da8fd96 | ||
|
|
e52f527eea |
@@ -122,6 +122,9 @@
|
|||||||
<SubType>Code</SubType>
|
<SubType>Code</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Remove="Pages\Accounts\AccountsPopupPage.xaml.cs" />
|
<Compile Remove="Pages\Accounts\AccountsPopupPage.xaml.cs" />
|
||||||
|
<Compile Update="Pages\Accounts\LoginPasswordlessPage.xaml.cs">
|
||||||
|
<DependentUpon>LoginPasswordlessPage.xaml</DependentUpon>
|
||||||
|
</Compile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
90
src/App/Pages/Accounts/LoginPasswordlessPage.xaml
Normal file
90
src/App/Pages/Accounts/LoginPasswordlessPage.xaml
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
<?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.LoginPasswordlessPage"
|
||||||
|
xmlns:pages="clr-namespace:Bit.App.Pages"
|
||||||
|
xmlns:controls="clr-namespace:Bit.App.Controls"
|
||||||
|
xmlns:u="clr-namespace:Bit.App.Utilities"
|
||||||
|
x:DataType="pages:LoginPasswordlessViewModel"
|
||||||
|
Title="{Binding PageTitle}">
|
||||||
|
|
||||||
|
<ContentPage.BindingContext>
|
||||||
|
<pages:LoginPasswordlessViewModel />
|
||||||
|
</ContentPage.BindingContext>
|
||||||
|
|
||||||
|
<ContentPage.ToolbarItems>
|
||||||
|
<ToolbarItem Text="{u:I18n Close}" Clicked="Close_Clicked" Order="Primary" />
|
||||||
|
</ContentPage.ToolbarItems>
|
||||||
|
|
||||||
|
<ContentPage.Resources>
|
||||||
|
<ResourceDictionary>
|
||||||
|
<u:InverseBoolConverter x:Key="inverseBool" />
|
||||||
|
<u:StringHasValueConverter x:Key="stringHasValue" />
|
||||||
|
<u:IsNotNullConverter x:Key="notNull" />
|
||||||
|
</ResourceDictionary>
|
||||||
|
</ContentPage.Resources>
|
||||||
|
<ScrollView x:Name="_scrollView" Padding="7, 0, 7, 20">
|
||||||
|
<StackLayout>
|
||||||
|
<Label
|
||||||
|
Text="{u:I18n AreYouTryingToLogIn}"
|
||||||
|
FontSize="Title"
|
||||||
|
FontAttributes="Bold"
|
||||||
|
Margin="0,14,0,21"/>
|
||||||
|
<Label
|
||||||
|
Text="{Binding LogInAttempByLabel}"
|
||||||
|
FontSize="Small"
|
||||||
|
Margin="0,0,0,24"/>
|
||||||
|
<Label
|
||||||
|
Text="{u:I18n FingerprintPhrase}"
|
||||||
|
FontSize="Small"
|
||||||
|
FontAttributes="Bold"/>
|
||||||
|
<controls:MonoLabel
|
||||||
|
FormattedText="{Binding FingerprintPhraseFormatted}"
|
||||||
|
FontSize="Medium"
|
||||||
|
Margin="0,0,0,27"/>
|
||||||
|
<Label
|
||||||
|
Text="{u:I18n DeviceType}"
|
||||||
|
FontSize="Small"
|
||||||
|
FontAttributes="Bold"/>
|
||||||
|
<Label
|
||||||
|
Text="{Binding DeviceType}"
|
||||||
|
FontSize="Small"
|
||||||
|
Margin="0,0,0,21"/>
|
||||||
|
<Label
|
||||||
|
Text="{u:I18n IpAddress}"
|
||||||
|
FontSize="Small"
|
||||||
|
FontAttributes="Bold"/>
|
||||||
|
<Label
|
||||||
|
Text="{Binding IpAddress}"
|
||||||
|
FontSize="Small"
|
||||||
|
Margin="0,0,0,21"/>
|
||||||
|
<Label
|
||||||
|
Text="{u:I18n Near}"
|
||||||
|
FontSize="Small"
|
||||||
|
FontAttributes="Bold"/>
|
||||||
|
<Label
|
||||||
|
Text="{Binding NearLocation}"
|
||||||
|
FontSize="Small"
|
||||||
|
Margin="0,0,0,21"/>
|
||||||
|
<Label
|
||||||
|
Text="{u:I18n Time}"
|
||||||
|
FontSize="Small"
|
||||||
|
FontAttributes="Bold"/>
|
||||||
|
<Label
|
||||||
|
Text="{Binding TimeOfRequestText}"
|
||||||
|
FontSize="Small"
|
||||||
|
Margin="0,0,0,57"/>
|
||||||
|
<Button
|
||||||
|
Text="{u:I18n ConfirmLogIn}"
|
||||||
|
Command="{Binding AcceptRequestCommand}"
|
||||||
|
Margin="0,0,0,17"
|
||||||
|
StyleClass="btn-primary"/>
|
||||||
|
<Button
|
||||||
|
Text="{u:I18n DenyLogIn}"
|
||||||
|
Command="{Binding RejectRequestCommand}"
|
||||||
|
StyleClass="btn-secundary"/>
|
||||||
|
</StackLayout>
|
||||||
|
</ScrollView>
|
||||||
|
|
||||||
|
</pages:BaseContentPage>
|
||||||
36
src/App/Pages/Accounts/LoginPasswordlessPage.xaml.cs
Normal file
36
src/App/Pages/Accounts/LoginPasswordlessPage.xaml.cs
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Bit.App.Pages
|
||||||
|
{
|
||||||
|
public partial class LoginPasswordlessPage : BaseContentPage
|
||||||
|
{
|
||||||
|
private LoginPasswordlessViewModel _vm;
|
||||||
|
|
||||||
|
public LoginPasswordlessPage(string fingerprintPhrase, string email, string deviceType, string ipAddress, string location, DateTime requestDate)
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
_vm = BindingContext as LoginPasswordlessViewModel;
|
||||||
|
_vm.Page = this;
|
||||||
|
_vm.Email = email;
|
||||||
|
_vm.DeviceType = deviceType;
|
||||||
|
_vm.IpAddress = ipAddress;
|
||||||
|
_vm.NearLocation = location;
|
||||||
|
_vm.FingerprintPhrase = fingerprintPhrase;
|
||||||
|
_vm.RequestDate = requestDate;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void Close_Clicked(object sender, System.EventArgs e)
|
||||||
|
{
|
||||||
|
await Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Close()
|
||||||
|
{
|
||||||
|
if (DoOnce())
|
||||||
|
{
|
||||||
|
await Navigation.PopModalAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
189
src/App/Pages/Accounts/LoginPasswordlessViewModel.cs
Normal file
189
src/App/Pages/Accounts/LoginPasswordlessViewModel.cs
Normal file
@@ -0,0 +1,189 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Bit.App.Resources;
|
||||||
|
using Bit.Core.Abstractions;
|
||||||
|
using Bit.Core.Utilities;
|
||||||
|
using Xamarin.Forms;
|
||||||
|
using Bit.App.Utilities;
|
||||||
|
using System.Linq;
|
||||||
|
using Xamarin.CommunityToolkit.ObjectModel;
|
||||||
|
using System.Windows.Input;
|
||||||
|
|
||||||
|
namespace Bit.App.Pages
|
||||||
|
{
|
||||||
|
public class LoginPasswordlessViewModel : BaseViewModel
|
||||||
|
{
|
||||||
|
private IAuthService _authService;
|
||||||
|
private IPlatformUtilsService _platformUtilsService;
|
||||||
|
private ILogger _logger;
|
||||||
|
private string _logInAttempByLabel;
|
||||||
|
private string _deviceType;
|
||||||
|
private FormattedString _fingerprintPhraseFormatted;
|
||||||
|
private string _fingerprintPhrase;
|
||||||
|
private string _email;
|
||||||
|
private string _timeOfRequest;
|
||||||
|
private DateTime _requestDate;
|
||||||
|
private string _nearLocation;
|
||||||
|
private string _ipAddress;
|
||||||
|
|
||||||
|
public LoginPasswordlessViewModel()
|
||||||
|
{
|
||||||
|
_authService = ServiceContainer.Resolve<IAuthService>("authService");
|
||||||
|
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||||
|
_logger = ServiceContainer.Resolve<ILogger>("logger");
|
||||||
|
|
||||||
|
PageTitle = AppResources.LogInRequested;
|
||||||
|
|
||||||
|
AcceptRequestCommand = new AsyncCommand(AcceptRequestAsync,
|
||||||
|
onException: ex => _logger.Exception(ex),
|
||||||
|
allowsMultipleExecutions: false);
|
||||||
|
RejectRequestCommand = new AsyncCommand(RejectRequestAsync,
|
||||||
|
onException: ex => _logger.Exception(ex),
|
||||||
|
allowsMultipleExecutions: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ICommand AcceptRequestCommand { get; }
|
||||||
|
|
||||||
|
public ICommand RejectRequestCommand { get; }
|
||||||
|
|
||||||
|
public string Email
|
||||||
|
{
|
||||||
|
get => _email;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
LogInAttempByLabel = string.Format(AppResources.LogInAttemptByOn, value, "bitwarden login test");
|
||||||
|
SetProperty(ref _email, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string FingerprintPhrase
|
||||||
|
{
|
||||||
|
get => _fingerprintPhrase;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
FingerprintPhraseFormatted = CreateFingerprintPhrase(value);
|
||||||
|
SetProperty(ref _fingerprintPhrase, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public FormattedString FingerprintPhraseFormatted
|
||||||
|
{
|
||||||
|
get => _fingerprintPhraseFormatted;
|
||||||
|
set => SetProperty(ref _fingerprintPhraseFormatted, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string LogInAttempByLabel
|
||||||
|
{
|
||||||
|
get => _logInAttempByLabel;
|
||||||
|
set => SetProperty(ref _logInAttempByLabel, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string DeviceType
|
||||||
|
{
|
||||||
|
get => _deviceType;
|
||||||
|
set => SetProperty(ref _deviceType, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string IpAddress
|
||||||
|
{
|
||||||
|
get => _ipAddress;
|
||||||
|
set => SetProperty(ref _ipAddress, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string NearLocation
|
||||||
|
{
|
||||||
|
get => _nearLocation;
|
||||||
|
set => SetProperty(ref _nearLocation, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DateTime RequestDate
|
||||||
|
{
|
||||||
|
get => _requestDate;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
TimeOfRequestText = CreateRequestDate();
|
||||||
|
SetProperty(ref _requestDate, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string TimeOfRequestText
|
||||||
|
{
|
||||||
|
get => _timeOfRequest;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
SetProperty(ref _timeOfRequest, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private FormattedString CreateFingerprintPhrase(string fingerprintPhrase)
|
||||||
|
{
|
||||||
|
var fingerprintList = fingerprintPhrase.Split('-').ToList();
|
||||||
|
var fs = new FormattedString();
|
||||||
|
var lastFingerprint = fingerprintList.LastOrDefault();
|
||||||
|
|
||||||
|
foreach (var fingerprint in fingerprintList)
|
||||||
|
{
|
||||||
|
fs.Spans.Add(new Span
|
||||||
|
{
|
||||||
|
Text = fingerprint
|
||||||
|
});
|
||||||
|
|
||||||
|
if(fingerprint == lastFingerprint)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.Spans.Add(new Span
|
||||||
|
{
|
||||||
|
Text = "-",
|
||||||
|
TextColor = ThemeManager.GetResourceColor("DangerColor")
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return fs;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string CreateRequestDate()
|
||||||
|
{
|
||||||
|
var minutesSinceRequest = RequestDate.ToUniversalTime().Minute - DateTime.UtcNow.Minute;
|
||||||
|
if(minutesSinceRequest < 5)
|
||||||
|
{
|
||||||
|
return AppResources.JustNow;
|
||||||
|
}
|
||||||
|
if(minutesSinceRequest < 59)
|
||||||
|
{
|
||||||
|
return $"{minutesSinceRequest} {AppResources.MinutesAgo}";
|
||||||
|
}
|
||||||
|
|
||||||
|
return RequestDate.ToShortTimeString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task AcceptRequestAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var res = await _authService.LogInPasswordlessAcceptAsync();
|
||||||
|
await ((LoginPasswordlessPage)this.Page).Close();
|
||||||
|
_platformUtilsService.ShowToast("info", null, AppResources.LogInAccepted);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.Exception(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task RejectRequestAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var res = await _authService.LogInPasswordlessRejectAsync();
|
||||||
|
await ((LoginPasswordlessPage)this.Page).Close();
|
||||||
|
_platformUtilsService.ShowToast("info", null, AppResources.LogInDenied);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.Exception(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
9333
src/App/Resources/AppResources.Designer.cs
generated
9333
src/App/Resources/AppResources.Designer.cs
generated
File diff suppressed because it is too large
Load Diff
@@ -2269,4 +2269,46 @@
|
|||||||
<data name="AreYouSureYouWantToEnableScreenCapture" xml:space="preserve">
|
<data name="AreYouSureYouWantToEnableScreenCapture" xml:space="preserve">
|
||||||
<value>Are you sure you want to enable Screen Capture?</value>
|
<value>Are you sure you want to enable Screen Capture?</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="LogInRequested" xml:space="preserve">
|
||||||
|
<value>Log in requested</value>
|
||||||
|
</data>
|
||||||
|
<data name="AreYouTryingToLogIn" xml:space="preserve">
|
||||||
|
<value>Are you trying to log in?</value>
|
||||||
|
</data>
|
||||||
|
<data name="LogInAttemptByOn" xml:space="preserve">
|
||||||
|
<value>Log in attempt by {0} on {1}</value>
|
||||||
|
</data>
|
||||||
|
<data name="FingerprintPhrase" xml:space="preserve">
|
||||||
|
<value>Fingerprint phrase</value>
|
||||||
|
</data>
|
||||||
|
<data name="DeviceType" xml:space="preserve">
|
||||||
|
<value>Device Type</value>
|
||||||
|
</data>
|
||||||
|
<data name="IpAddress" xml:space="preserve">
|
||||||
|
<value>IP Address</value>
|
||||||
|
</data>
|
||||||
|
<data name="Time" xml:space="preserve">
|
||||||
|
<value>Time</value>
|
||||||
|
</data>
|
||||||
|
<data name="Near" xml:space="preserve">
|
||||||
|
<value>Near</value>
|
||||||
|
</data>
|
||||||
|
<data name="ConfirmLogIn" xml:space="preserve">
|
||||||
|
<value>Confirm Log In</value>
|
||||||
|
</data>
|
||||||
|
<data name="DenyLogIn" xml:space="preserve">
|
||||||
|
<value>Deny Log In</value>
|
||||||
|
</data>
|
||||||
|
<data name="JustNow" xml:space="preserve">
|
||||||
|
<value>Just Now</value>
|
||||||
|
</data>
|
||||||
|
<data name="MinutesAgo" xml:space="preserve">
|
||||||
|
<value>minutes ago</value>
|
||||||
|
</data>
|
||||||
|
<data name="LogInAccepted" xml:space="preserve">
|
||||||
|
<value>Log in accepted</value>
|
||||||
|
</data>
|
||||||
|
<data name="LogInDenied" xml:space="preserve">
|
||||||
|
<value>Log in denied</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|||||||
@@ -25,6 +25,11 @@ namespace Bit.Core.Abstractions
|
|||||||
Task<AuthResult> LogInSsoAsync(string code, string codeVerifier, string redirectUrl, string orgId);
|
Task<AuthResult> LogInSsoAsync(string code, string codeVerifier, string redirectUrl, string orgId);
|
||||||
Task<AuthResult> LogInCompleteAsync(string email, string masterPassword, TwoFactorProviderType twoFactorProvider, string twoFactorToken, bool? remember = null);
|
Task<AuthResult> LogInCompleteAsync(string email, string masterPassword, TwoFactorProviderType twoFactorProvider, string twoFactorToken, bool? remember = null);
|
||||||
Task<AuthResult> LogInTwoFactorAsync(TwoFactorProviderType twoFactorProvider, string twoFactorToken, string captchaToken, bool? remember = null);
|
Task<AuthResult> LogInTwoFactorAsync(TwoFactorProviderType twoFactorProvider, string twoFactorToken, string captchaToken, bool? remember = null);
|
||||||
|
|
||||||
|
Task<AuthResult> GetLogInPasswordlessRequestsAsync();
|
||||||
|
Task<AuthResult> LogInPasswordlessAcceptAsync();
|
||||||
|
Task<AuthResult> LogInPasswordlessRejectAsync();
|
||||||
|
|
||||||
void LogOut(Action callback);
|
void LogOut(Action callback);
|
||||||
void Init();
|
void Init();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -468,5 +468,9 @@ namespace Bit.Core.Services
|
|||||||
TwoFactorProvidersData = null;
|
TwoFactorProvidersData = null;
|
||||||
SelectedTwoFactorProviderType = null;
|
SelectedTwoFactorProviderType = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<AuthResult> GetLogInPasswordlessRequestsAsync() => await Task.FromResult<AuthResult>(new AuthResult());
|
||||||
|
public async Task<AuthResult> LogInPasswordlessAcceptAsync() => await Task.FromResult<AuthResult>(new AuthResult());
|
||||||
|
public async Task<AuthResult> LogInPasswordlessRejectAsync() => await Task.FromResult<AuthResult>(new AuthResult());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user