diff --git a/src/App/Abstractions/Services/IAuthService.cs b/src/App/Abstractions/Services/IAuthService.cs
index 56246af7a..4e743c3ad 100644
--- a/src/App/Abstractions/Services/IAuthService.cs
+++ b/src/App/Abstractions/Services/IAuthService.cs
@@ -8,6 +8,7 @@ namespace Bit.App.Abstractions
bool IsAuthenticated { get; }
string Token { get; set; }
string UserId { get; set; }
+ string Email { get; set; }
string PIN { get; set; }
void LogOut();
diff --git a/src/App/App.cs b/src/App/App.cs
index 4f77aeefa..32b04d7df 100644
--- a/src/App/App.cs
+++ b/src/App/App.cs
@@ -216,7 +216,10 @@ namespace Bit.App
}
else
{
- // Use master password to unlock if no other methods are set
+ if((currentPage?.CurrentPage as LockPasswordPage) == null)
+ {
+ await Current.MainPage.Navigation.PushModalAsync(new ExtendedNavigationPage(new LockPasswordPage()), false);
+ }
}
}
diff --git a/src/App/App.csproj b/src/App/App.csproj
index d78acfb40..33c08fb2e 100644
--- a/src/App/App.csproj
+++ b/src/App/App.csproj
@@ -107,6 +107,7 @@
+
diff --git a/src/App/Pages/Lock/LockPasswordPage.cs b/src/App/Pages/Lock/LockPasswordPage.cs
new file mode 100644
index 000000000..ee572df70
--- /dev/null
+++ b/src/App/Pages/Lock/LockPasswordPage.cs
@@ -0,0 +1,116 @@
+using System;
+using System.Threading.Tasks;
+using Acr.UserDialogs;
+using Bit.App.Abstractions;
+using Bit.App.Resources;
+using Xamarin.Forms;
+using XLabs.Ioc;
+using Plugin.Settings.Abstractions;
+using Bit.App.Models.Page;
+using Bit.App.Controls;
+using System.Linq;
+
+namespace Bit.App.Pages
+{
+ public class LockPasswordPage : ExtendedContentPage
+ {
+ private readonly IAuthService _authService;
+ private readonly IUserDialogs _userDialogs;
+ private readonly ISettings _settings;
+ private readonly ICryptoService _cryptoService;
+
+ public LockPasswordPage()
+ : base(false)
+ {
+ _authService = Resolver.Resolve();
+ _userDialogs = Resolver.Resolve();
+ _settings = Resolver.Resolve();
+ _cryptoService = Resolver.Resolve();
+
+ Init();
+ }
+
+ public Entry Password { get; private set; }
+
+ public void Init()
+ {
+ Password = new ExtendedEntry
+ {
+ HasBorder = false,
+ IsPassword = true,
+ Placeholder = AppResources.MasterPassword,
+ ReturnType = Enums.ReturnType.Go
+ };
+
+ var logoutButton = new Button
+ {
+ Text = AppResources.LogOut,
+ Command = new Command(async () => await LogoutAsync()),
+ VerticalOptions = LayoutOptions.End,
+ Style = (Style)Application.Current.Resources["btn-primaryAccent"]
+ };
+
+ var stackLayout = new StackLayout
+ {
+ Padding = new Thickness(30, 40),
+ Spacing = 10,
+ Children = { Password, logoutButton }
+ };
+
+ var loginToolbarItem = new ToolbarItem("Submit", null, async () =>
+ {
+ await CheckPasswordAsync();
+ }, ToolbarItemOrder.Default, 0);
+
+ ToolbarItems.Add(loginToolbarItem);
+ Title = "Verify Master Password";
+ Content = stackLayout;
+ }
+
+ protected override bool OnBackButtonPressed()
+ {
+ return false;
+ }
+
+ protected override void OnAppearing()
+ {
+ base.OnAppearing();
+ Password.Focus();
+ }
+
+ protected async Task CheckPasswordAsync()
+ {
+ if(string.IsNullOrWhiteSpace(Password.Text))
+ {
+ await DisplayAlert(AppResources.AnErrorHasOccurred, string.Format(AppResources.ValidationFieldRequired, AppResources.MasterPassword), AppResources.Ok);
+ return;
+ }
+
+ var key = _cryptoService.MakeKeyFromPassword(Password.Text, _authService.Email);
+ if(key.SequenceEqual(_cryptoService.Key))
+ {
+ await Navigation.PopModalAsync();
+ }
+ else
+ {
+ // TODO: keep track of invalid attempts and logout?
+
+ _userDialogs.Alert("Invalid Master Password. Try again.");
+ Password.Text = string.Empty;
+ Password.Focus();
+ }
+ }
+
+ private async Task LogoutAsync()
+ {
+ if(!await _userDialogs.ConfirmAsync("Are you sure you want to log out?", null, AppResources.Yes, AppResources.Cancel))
+ {
+ return;
+ }
+
+ _authService.LogOut();
+ await Navigation.PopModalAsync();
+ Application.Current.MainPage = new HomePage();
+ }
+ }
+}
diff --git a/src/App/Pages/LoginPage.cs b/src/App/Pages/LoginPage.cs
index 41e0cb315..4419f8b67 100644
--- a/src/App/Pages/LoginPage.cs
+++ b/src/App/Pages/LoginPage.cs
@@ -129,6 +129,7 @@ namespace Bit.App.Pages
_cryptoService.Key = key;
_authService.Token = response.Result.Token;
_authService.UserId = response.Result.Profile.Id;
+ _authService.Email = response.Result.Profile.Email;
var syncTask = _syncService.FullSyncAsync();
Application.Current.MainPage = new MainPage();
diff --git a/src/App/Pages/Vault/VaultListSitesPage.cs b/src/App/Pages/Vault/VaultListSitesPage.cs
index 3cf0401e5..5241b2a97 100644
--- a/src/App/Pages/Vault/VaultListSitesPage.cs
+++ b/src/App/Pages/Vault/VaultListSitesPage.cs
@@ -85,7 +85,8 @@ namespace Bit.App.Pages
Search = new SearchBar
{
Placeholder = "Search vault",
- FontSize = Device.GetNamedSize(NamedSize.Small, typeof(Button))
+ FontSize = Device.GetNamedSize(NamedSize.Small, typeof(Button)),
+ CancelButtonColor = Color.FromHex("3c8dbc")
};
Search.TextChanged += SearchBar_TextChanged;
Search.SearchButtonPressed += SearchBar_SearchButtonPressed;
diff --git a/src/App/Services/AuthService.cs b/src/App/Services/AuthService.cs
index 6cd273f4d..f13eacd08 100644
--- a/src/App/Services/AuthService.cs
+++ b/src/App/Services/AuthService.cs
@@ -10,6 +10,7 @@ namespace Bit.App.Services
public class AuthService : IAuthService
{
private const string TokenKey = "token";
+ private const string EmailKey = "email";
private const string UserIdKey = "userId";
private const string PinKey = "pin";
@@ -19,6 +20,7 @@ namespace Bit.App.Services
private readonly IAuthApiRepository _authApiRepository;
private string _token;
+ private string _email;
private string _userId;
private string _pin;
@@ -95,6 +97,33 @@ namespace Bit.App.Services
}
}
+ public string Email
+ {
+ get
+ {
+ if(_email != null)
+ {
+ return _email;
+ }
+
+ _email = _settings.GetValueOrDefault(EmailKey);
+ return _email;
+ }
+ set
+ {
+ if(value != null)
+ {
+ _settings.AddOrUpdateValue(EmailKey, value);
+ }
+ else
+ {
+ _settings.Remove(EmailKey);
+ }
+
+ _email = value;
+ }
+ }
+
public bool IsAuthenticated
{
get
@@ -141,6 +170,7 @@ namespace Bit.App.Services
{
Token = null;
UserId = null;
+ Email = null;
_cryptoService.Key = null;
}