mirror of
https://github.com/bitwarden/android.git
synced 2024-10-31 15:15:34 +03:00
refactors for new 2fa flows
This commit is contained in:
parent
35ae2b783f
commit
4116d95a3e
10 changed files with 254 additions and 95 deletions
|
@ -1,4 +1,5 @@
|
||||||
using Bit.App.Models;
|
using Bit.App.Enums;
|
||||||
|
using Bit.App.Models;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Bit.App.Abstractions
|
namespace Bit.App.Abstractions
|
||||||
|
@ -15,6 +16,7 @@ namespace Bit.App.Abstractions
|
||||||
bool BelongsToOrganization(string orgId);
|
bool BelongsToOrganization(string orgId);
|
||||||
void LogOut();
|
void LogOut();
|
||||||
Task<FullLoginResult> TokenPostAsync(string email, string masterPassword);
|
Task<FullLoginResult> TokenPostAsync(string email, string masterPassword);
|
||||||
Task<LoginResult> TokenPostTwoFactorAsync(string token, string email, string masterPasswordHash, SymmetricCryptoKey key);
|
Task<LoginResult> TokenPostTwoFactorAsync(TwoFactorProviderType type, string token, bool remember, string email,
|
||||||
|
string masterPasswordHash, SymmetricCryptoKey key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,6 +81,7 @@
|
||||||
<Compile Include="Controls\FormEntryCell.cs" />
|
<Compile Include="Controls\FormEntryCell.cs" />
|
||||||
<Compile Include="Controls\PinControl.cs" />
|
<Compile Include="Controls\PinControl.cs" />
|
||||||
<Compile Include="Controls\VaultListViewCell.cs" />
|
<Compile Include="Controls\VaultListViewCell.cs" />
|
||||||
|
<Compile Include="Enums\TwoFactorProviderType.cs" />
|
||||||
<Compile Include="Enums\EncryptionType.cs" />
|
<Compile Include="Enums\EncryptionType.cs" />
|
||||||
<Compile Include="Enums\OrganizationUserType.cs" />
|
<Compile Include="Enums\OrganizationUserType.cs" />
|
||||||
<Compile Include="Enums\LockType.cs" />
|
<Compile Include="Enums\LockType.cs" />
|
||||||
|
|
12
src/App/Enums/TwoFactorProviderType.cs
Normal file
12
src/App/Enums/TwoFactorProviderType.cs
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
namespace Bit.App.Enums
|
||||||
|
{
|
||||||
|
public enum TwoFactorProviderType : byte
|
||||||
|
{
|
||||||
|
Authenticator = 0,
|
||||||
|
Email = 1,
|
||||||
|
Duo = 2,
|
||||||
|
YubiKey = 3,
|
||||||
|
U2f = 4,
|
||||||
|
Remember = 5
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
using System;
|
using Bit.App.Enums;
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Bit.App.Models.Api
|
namespace Bit.App.Models.Api
|
||||||
|
@ -8,10 +9,11 @@ namespace Bit.App.Models.Api
|
||||||
public string Email { get; set; }
|
public string Email { get; set; }
|
||||||
public string MasterPasswordHash { get; set; }
|
public string MasterPasswordHash { get; set; }
|
||||||
public string Token { get; set; }
|
public string Token { get; set; }
|
||||||
public int? Provider { get; set; }
|
public TwoFactorProviderType? Provider { get; set; }
|
||||||
[Obsolete]
|
[Obsolete]
|
||||||
public string OldAuthBearer { get; set; }
|
public string OldAuthBearer { get; set; }
|
||||||
public DeviceRequest Device { get; set; }
|
public DeviceRequest Device { get; set; }
|
||||||
|
public bool Remember { get; set; }
|
||||||
|
|
||||||
public IDictionary<string, string> ToIdentityTokenRequest()
|
public IDictionary<string, string> ToIdentityTokenRequest()
|
||||||
{
|
{
|
||||||
|
@ -40,7 +42,8 @@ namespace Bit.App.Models.Api
|
||||||
if(Token != null && Provider.HasValue)
|
if(Token != null && Provider.HasValue)
|
||||||
{
|
{
|
||||||
dict.Add("TwoFactorToken", Token);
|
dict.Add("TwoFactorToken", Token);
|
||||||
dict.Add("TwoFactorProvider", Provider.Value.ToString());
|
dict.Add("TwoFactorProvider", ((byte)(Provider.Value)).ToString());
|
||||||
|
dict.Add("TwoFactorRemember", Remember ? "1" : "0");
|
||||||
}
|
}
|
||||||
|
|
||||||
return dict;
|
return dict;
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using Newtonsoft.Json;
|
using Bit.App.Enums;
|
||||||
|
using Newtonsoft.Json;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Bit.App.Models.Api
|
namespace Bit.App.Models.Api
|
||||||
|
@ -13,7 +14,7 @@ namespace Bit.App.Models.Api
|
||||||
public string RefreshToken { get; set; }
|
public string RefreshToken { get; set; }
|
||||||
[JsonProperty("token_type")]
|
[JsonProperty("token_type")]
|
||||||
public string TokenType { get; set; }
|
public string TokenType { get; set; }
|
||||||
public List<int> TwoFactorProviders { get; set; }
|
public Dictionary<TwoFactorProviderType, Dictionary<string, object>> TwoFactorProviders2 { get; set; }
|
||||||
public string PrivateKey { get; set; }
|
public string PrivateKey { get; set; }
|
||||||
public string Key { get; set; }
|
public string Key { get; set; }
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
namespace Bit.App.Models
|
using Bit.App.Enums;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Bit.App.Models
|
||||||
{
|
{
|
||||||
public class LoginResult
|
public class LoginResult
|
||||||
{
|
{
|
||||||
|
@ -8,7 +11,8 @@
|
||||||
|
|
||||||
public class FullLoginResult : LoginResult
|
public class FullLoginResult : LoginResult
|
||||||
{
|
{
|
||||||
public bool TwoFactorRequired { get; set; }
|
public bool TwoFactorRequired => TwoFactorProviders != null && TwoFactorProviders.Count > 0;
|
||||||
|
public Dictionary<TwoFactorProviderType, Dictionary<string, object>> TwoFactorProviders { get; set; }
|
||||||
public SymmetricCryptoKey Key { get; set; }
|
public SymmetricCryptoKey Key { get; set; }
|
||||||
public string MasterPasswordHash { get; set; }
|
public string MasterPasswordHash { get; set; }
|
||||||
}
|
}
|
||||||
|
|
|
@ -192,7 +192,7 @@ namespace Bit.App.Pages
|
||||||
if(result.TwoFactorRequired)
|
if(result.TwoFactorRequired)
|
||||||
{
|
{
|
||||||
_googleAnalyticsService.TrackAppEvent("LoggedIn To Two-step");
|
_googleAnalyticsService.TrackAppEvent("LoggedIn To Two-step");
|
||||||
await Navigation.PushAsync(new LoginTwoFactorPage(EmailCell.Entry.Text, result.MasterPasswordHash, result.Key));
|
await Navigation.PushAsync(new LoginTwoFactorPage(EmailCell.Entry.Text, result));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,9 @@ using System.Threading.Tasks;
|
||||||
using PushNotification.Plugin.Abstractions;
|
using PushNotification.Plugin.Abstractions;
|
||||||
using Bit.App.Models;
|
using Bit.App.Models;
|
||||||
using Bit.App.Utilities;
|
using Bit.App.Utilities;
|
||||||
|
using Bit.App.Enums;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace Bit.App.Pages
|
namespace Bit.App.Pages
|
||||||
{
|
{
|
||||||
|
@ -22,13 +25,17 @@ namespace Bit.App.Pages
|
||||||
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 TwoFactorProviderType? _providerType;
|
||||||
|
|
||||||
public LoginTwoFactorPage(string email, string masterPasswordHash, SymmetricCryptoKey key)
|
public LoginTwoFactorPage(string email, FullLoginResult result, TwoFactorProviderType? type = null)
|
||||||
: base(updateActivity: false)
|
: base(updateActivity: false)
|
||||||
{
|
{
|
||||||
_email = email;
|
_email = email;
|
||||||
_masterPasswordHash = masterPasswordHash;
|
_masterPasswordHash = result.MasterPasswordHash;
|
||||||
_key = key;
|
_key = result.Key;
|
||||||
|
_providers = result.TwoFactorProviders;
|
||||||
|
_providerType = type ?? GetDefaultProvider();
|
||||||
|
|
||||||
_authService = Resolver.Resolve<IAuthService>();
|
_authService = Resolver.Resolve<IAuthService>();
|
||||||
_userDialogs = Resolver.Resolve<IUserDialogs>();
|
_userDialogs = Resolver.Resolve<IUserDialogs>();
|
||||||
|
@ -39,20 +46,48 @@ namespace Bit.App.Pages
|
||||||
Init();
|
Init();
|
||||||
}
|
}
|
||||||
|
|
||||||
public FormEntryCell CodeCell { get; set; }
|
public FormEntryCell TokenCell { get; set; }
|
||||||
|
public ExtendedSwitchCell RememberCell { get; set; }
|
||||||
|
|
||||||
private void Init()
|
private void Init()
|
||||||
|
{
|
||||||
|
var scrollView = new ScrollView();
|
||||||
|
|
||||||
|
var continueToolbarItem = new ToolbarItem(AppResources.Continue, null, async () =>
|
||||||
|
{
|
||||||
|
var token = TokenCell?.Entry.Text.Trim().Replace(" ", "");
|
||||||
|
await LogInAsync(token);
|
||||||
|
}, ToolbarItemOrder.Default, 0);
|
||||||
|
|
||||||
|
if(!_providerType.HasValue)
|
||||||
|
{
|
||||||
|
var noProviderLabel = new Label
|
||||||
|
{
|
||||||
|
Text = "No provider.",
|
||||||
|
LineBreakMode = LineBreakMode.WordWrap,
|
||||||
|
Margin = new Thickness(15),
|
||||||
|
HorizontalTextAlignment = TextAlignment.Center
|
||||||
|
};
|
||||||
|
scrollView.Content = noProviderLabel;
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
var padding = Helpers.OnPlatform(
|
var padding = Helpers.OnPlatform(
|
||||||
iOS: new Thickness(15, 20),
|
iOS: new Thickness(15, 20),
|
||||||
Android: new Thickness(15, 8),
|
Android: new Thickness(15, 8),
|
||||||
WinPhone: new Thickness(15, 20));
|
WinPhone: new Thickness(15, 20));
|
||||||
|
|
||||||
CodeCell = new FormEntryCell(AppResources.VerificationCode, useLabelAsPlaceholder: true,
|
TokenCell = new FormEntryCell(AppResources.VerificationCode, useLabelAsPlaceholder: true,
|
||||||
imageSource: "lock", containerPadding: padding);
|
imageSource: "lock", containerPadding: padding);
|
||||||
|
|
||||||
CodeCell.Entry.Keyboard = Keyboard.Numeric;
|
TokenCell.Entry.Keyboard = Keyboard.Numeric;
|
||||||
CodeCell.Entry.ReturnType = Enums.ReturnType.Go;
|
TokenCell.Entry.ReturnType = ReturnType.Go;
|
||||||
|
|
||||||
|
RememberCell = new ExtendedSwitchCell
|
||||||
|
{
|
||||||
|
Text = "Remember me",
|
||||||
|
On = false
|
||||||
|
};
|
||||||
|
|
||||||
var table = new ExtendedTableView
|
var table = new ExtendedTableView
|
||||||
{
|
{
|
||||||
|
@ -61,42 +96,18 @@ namespace Bit.App.Pages
|
||||||
HasUnevenRows = true,
|
HasUnevenRows = true,
|
||||||
EnableSelection = true,
|
EnableSelection = true,
|
||||||
NoFooter = true,
|
NoFooter = true,
|
||||||
|
NoHeader = true,
|
||||||
VerticalOptions = LayoutOptions.Start,
|
VerticalOptions = LayoutOptions.Start,
|
||||||
Root = new TableRoot
|
Root = new TableRoot
|
||||||
{
|
{
|
||||||
new TableSection(" ")
|
new TableSection(" ")
|
||||||
{
|
{
|
||||||
CodeCell
|
TokenCell,
|
||||||
|
RememberCell
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var codeLabel = new Label
|
|
||||||
{
|
|
||||||
Text = AppResources.EnterVerificationCode,
|
|
||||||
LineBreakMode = LineBreakMode.WordWrap,
|
|
||||||
FontSize = Device.GetNamedSize(NamedSize.Small, typeof(Label)),
|
|
||||||
Style = (Style)Application.Current.Resources["text-muted"],
|
|
||||||
Margin = new Thickness(15, (this.IsLandscape() ? 5 : 0), 15, 25)
|
|
||||||
};
|
|
||||||
|
|
||||||
var lostAppButton = new ExtendedButton
|
|
||||||
{
|
|
||||||
Text = AppResources.Lost2FAApp,
|
|
||||||
Style = (Style)Application.Current.Resources["btn-primaryAccent"],
|
|
||||||
Margin = new Thickness(15, 0, 15, 25),
|
|
||||||
Command = new Command(() => Lost2FAApp()),
|
|
||||||
Uppercase = false,
|
|
||||||
BackgroundColor = Color.Transparent
|
|
||||||
};
|
|
||||||
|
|
||||||
var layout = new StackLayout
|
|
||||||
{
|
|
||||||
Children = { table, codeLabel, lostAppButton },
|
|
||||||
Spacing = 0
|
|
||||||
};
|
|
||||||
|
|
||||||
var scrollView = new ScrollView { Content = layout };
|
|
||||||
|
|
||||||
if(Device.RuntimePlatform == Device.iOS)
|
if(Device.RuntimePlatform == Device.iOS)
|
||||||
{
|
{
|
||||||
|
@ -104,44 +115,123 @@ namespace Bit.App.Pages
|
||||||
table.EstimatedRowHeight = 70;
|
table.EstimatedRowHeight = 70;
|
||||||
}
|
}
|
||||||
|
|
||||||
var continueToolbarItem = new ToolbarItem(AppResources.Continue, null, async () =>
|
var instruction = new Label
|
||||||
{
|
{
|
||||||
await LogInAsync();
|
Text = AppResources.EnterVerificationCode,
|
||||||
}, ToolbarItemOrder.Default, 0);
|
LineBreakMode = LineBreakMode.WordWrap,
|
||||||
|
Margin = new Thickness(15),
|
||||||
|
HorizontalTextAlignment = TextAlignment.Center
|
||||||
|
};
|
||||||
|
|
||||||
|
var anotherMethodButton = new ExtendedButton
|
||||||
|
{
|
||||||
|
Text = "Use another two-step login method",
|
||||||
|
Style = (Style)Application.Current.Resources["btn-primaryAccent"],
|
||||||
|
Margin = new Thickness(15, 0, 15, 25),
|
||||||
|
Command = new Command(() => AnotherMethod()),
|
||||||
|
Uppercase = false,
|
||||||
|
BackgroundColor = Color.Transparent
|
||||||
|
};
|
||||||
|
|
||||||
|
var layout = new StackLayout
|
||||||
|
{
|
||||||
|
Children = { instruction, table, anotherMethodButton },
|
||||||
|
Spacing = 0
|
||||||
|
};
|
||||||
|
|
||||||
|
scrollView.Content = layout;
|
||||||
|
|
||||||
|
switch(_providerType.Value)
|
||||||
|
{
|
||||||
|
case TwoFactorProviderType.Authenticator:
|
||||||
|
instruction.Text = "Enter the 6 digit verification code from your authenticator app.";
|
||||||
|
layout.Children.Add(instruction);
|
||||||
|
layout.Children.Add(table);
|
||||||
|
layout.Children.Add(anotherMethodButton);
|
||||||
|
|
||||||
ToolbarItems.Add(continueToolbarItem);
|
ToolbarItems.Add(continueToolbarItem);
|
||||||
Title = AppResources.VerificationCode;
|
Title = AppResources.VerificationCode;
|
||||||
|
break;
|
||||||
|
case TwoFactorProviderType.Email:
|
||||||
|
var emailParams = _providers[TwoFactorProviderType.Email];
|
||||||
|
var redactedEmail = emailParams["Email"].ToString();
|
||||||
|
|
||||||
|
instruction.Text = "Enter the 6 digit verification code from your authenticator app.";
|
||||||
|
var resendEmailButton = new ExtendedButton
|
||||||
|
{
|
||||||
|
Text = $"Enter the 6 digit verification code that was emailed to {redactedEmail}.",
|
||||||
|
Style = (Style)Application.Current.Resources["btn-primaryAccent"],
|
||||||
|
Margin = new Thickness(15, 0, 15, 25),
|
||||||
|
Command = new Command(() => SendEmail()),
|
||||||
|
Uppercase = false,
|
||||||
|
BackgroundColor = Color.Transparent
|
||||||
|
};
|
||||||
|
|
||||||
|
layout.Children.Add(instruction);
|
||||||
|
layout.Children.Add(table);
|
||||||
|
layout.Children.Add(resendEmailButton);
|
||||||
|
layout.Children.Add(anotherMethodButton);
|
||||||
|
|
||||||
|
ToolbarItems.Add(continueToolbarItem);
|
||||||
|
Title = AppResources.VerificationCode;
|
||||||
|
break;
|
||||||
|
case TwoFactorProviderType.Duo:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Content = scrollView;
|
Content = scrollView;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnAppearing()
|
protected override void OnAppearing()
|
||||||
{
|
{
|
||||||
base.OnAppearing();
|
base.OnAppearing();
|
||||||
CodeCell.InitEvents();
|
|
||||||
CodeCell.Entry.FocusWithDelay();
|
if(TokenCell != null)
|
||||||
CodeCell.Entry.Completed += Entry_Completed;
|
{
|
||||||
|
TokenCell.InitEvents();
|
||||||
|
TokenCell.Entry.FocusWithDelay();
|
||||||
|
TokenCell.Entry.Completed += Entry_Completed;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnDisappearing()
|
protected override void OnDisappearing()
|
||||||
{
|
{
|
||||||
base.OnDisappearing();
|
base.OnDisappearing();
|
||||||
CodeCell.Dispose();
|
|
||||||
CodeCell.Entry.Completed -= Entry_Completed;
|
if(TokenCell != null)
|
||||||
|
{
|
||||||
|
TokenCell.Dispose();
|
||||||
|
TokenCell.Entry.Completed -= Entry_Completed;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Lost2FAApp()
|
private void AnotherMethod()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SendEmail()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Recover()
|
||||||
{
|
{
|
||||||
Device.OpenUri(new Uri("https://help.bitwarden.com/article/lost-two-step-device/"));
|
Device.OpenUri(new Uri("https://help.bitwarden.com/article/lost-two-step-device/"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void Entry_Completed(object sender, EventArgs e)
|
private async void Entry_Completed(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
await LogInAsync();
|
var token = TokenCell.Entry.Text.Trim().Replace(" ", "");
|
||||||
|
await LogInAsync(token);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task LogInAsync()
|
private async Task LogInAsync(string token)
|
||||||
{
|
{
|
||||||
if(string.IsNullOrWhiteSpace(CodeCell.Entry.Text))
|
if(string.IsNullOrWhiteSpace(token))
|
||||||
{
|
{
|
||||||
await DisplayAlert(AppResources.AnErrorHasOccurred, string.Format(AppResources.ValidationFieldRequired,
|
await DisplayAlert(AppResources.AnErrorHasOccurred, string.Format(AppResources.ValidationFieldRequired,
|
||||||
AppResources.VerificationCode), AppResources.Ok);
|
AppResources.VerificationCode), AppResources.Ok);
|
||||||
|
@ -149,7 +239,8 @@ namespace Bit.App.Pages
|
||||||
}
|
}
|
||||||
|
|
||||||
_userDialogs.ShowLoading(AppResources.ValidatingCode, MaskType.Black);
|
_userDialogs.ShowLoading(AppResources.ValidatingCode, MaskType.Black);
|
||||||
var response = await _authService.TokenPostTwoFactorAsync(CodeCell.Entry.Text, _email, _masterPasswordHash, _key);
|
var response = await _authService.TokenPostTwoFactorAsync(_providerType.Value, token, RememberCell.On,
|
||||||
|
_email, _masterPasswordHash, _key);
|
||||||
_userDialogs.HideLoading();
|
_userDialogs.HideLoading();
|
||||||
if(!response.Success)
|
if(!response.Success)
|
||||||
{
|
{
|
||||||
|
@ -167,5 +258,45 @@ namespace Bit.App.Pages
|
||||||
var task = Task.Run(async () => await _syncService.FullSyncAsync(true));
|
var task = Task.Run(async () => await _syncService.FullSyncAsync(true));
|
||||||
Application.Current.MainPage = new MainPage();
|
Application.Current.MainPage = new MainPage();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private TwoFactorProviderType? GetDefaultProvider()
|
||||||
|
{
|
||||||
|
TwoFactorProviderType? provider = null;
|
||||||
|
|
||||||
|
if(_providers != null)
|
||||||
|
{
|
||||||
|
if(_providers.Count == 1)
|
||||||
|
{
|
||||||
|
return _providers.First().Key;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach(var p in _providers)
|
||||||
|
{
|
||||||
|
switch(p.Key)
|
||||||
|
{
|
||||||
|
case TwoFactorProviderType.Authenticator:
|
||||||
|
if(provider == TwoFactorProviderType.Duo)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TwoFactorProviderType.Email:
|
||||||
|
if(provider.HasValue)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TwoFactorProviderType.Duo:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
provider = p.Key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return provider;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ using Plugin.Connectivity.Abstractions;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using Bit.App.Enums;
|
||||||
|
|
||||||
namespace Bit.App.Repositories
|
namespace Bit.App.Repositories
|
||||||
{
|
{
|
||||||
|
@ -46,11 +47,13 @@ namespace Bit.App.Repositories
|
||||||
if(!response.IsSuccessStatusCode)
|
if(!response.IsSuccessStatusCode)
|
||||||
{
|
{
|
||||||
var errorResponse = JObject.Parse(responseContent);
|
var errorResponse = JObject.Parse(responseContent);
|
||||||
if(errorResponse["TwoFactorProviders"] != null)
|
if(errorResponse["TwoFactorProviders2"] != null)
|
||||||
{
|
{
|
||||||
return ApiResult<TokenResponse>.Success(new TokenResponse
|
return ApiResult<TokenResponse>.Success(new TokenResponse
|
||||||
{
|
{
|
||||||
TwoFactorProviders = errorResponse["TwoFactorProviders"].ToObject<List<int>>()
|
TwoFactorProviders2 =
|
||||||
|
errorResponse["TwoFactorProviders2"]
|
||||||
|
.ToObject<Dictionary<TwoFactorProviderType, Dictionary<string, object>>>()
|
||||||
}, response.StatusCode);
|
}, response.StatusCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ using Bit.App.Models.Api;
|
||||||
using Plugin.Settings.Abstractions;
|
using Plugin.Settings.Abstractions;
|
||||||
using Bit.App.Models;
|
using Bit.App.Models;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Bit.App.Enums;
|
||||||
|
|
||||||
namespace Bit.App.Services
|
namespace Bit.App.Services
|
||||||
{
|
{
|
||||||
|
@ -230,11 +231,11 @@ namespace Bit.App.Services
|
||||||
}
|
}
|
||||||
|
|
||||||
result.Success = true;
|
result.Success = true;
|
||||||
if(response.Result.TwoFactorProviders != null && response.Result.TwoFactorProviders.Count > 0)
|
if(response.Result.TwoFactorProviders2 != null && response.Result.TwoFactorProviders2.Count > 0)
|
||||||
{
|
{
|
||||||
result.Key = key;
|
result.Key = key;
|
||||||
result.MasterPasswordHash = request.MasterPasswordHash;
|
result.MasterPasswordHash = request.MasterPasswordHash;
|
||||||
result.TwoFactorRequired = true;
|
result.TwoFactorProviders = response.Result.TwoFactorProviders2;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -242,17 +243,18 @@ namespace Bit.App.Services
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<LoginResult> TokenPostTwoFactorAsync(string token, string email, string masterPasswordHash,
|
public async Task<LoginResult> TokenPostTwoFactorAsync(TwoFactorProviderType type, string token, bool remember,
|
||||||
SymmetricCryptoKey key)
|
string email, string masterPasswordHash, SymmetricCryptoKey key)
|
||||||
{
|
{
|
||||||
var result = new LoginResult();
|
var result = new LoginResult();
|
||||||
|
|
||||||
var request = new TokenRequest
|
var request = new TokenRequest
|
||||||
{
|
{
|
||||||
|
Remember = remember,
|
||||||
Email = email.Trim().ToLower(),
|
Email = email.Trim().ToLower(),
|
||||||
MasterPasswordHash = masterPasswordHash,
|
MasterPasswordHash = masterPasswordHash,
|
||||||
Token = token.Trim().Replace(" ", ""),
|
Token = token,
|
||||||
Provider = 0, // Authenticator app (only 1 provider for now, so hard coded)
|
Provider = type,
|
||||||
Device = new DeviceRequest(_appIdService, _deviceInfoService)
|
Device = new DeviceRequest(_appIdService, _deviceInfoService)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue