mirror of
https://github.com/bitwarden/mobile.git
synced 2025-01-23 21:21:27 +01:00
PM-1575 Added non-discoverable passkeys to login UI
This commit is contained in:
parent
b88c0ea61f
commit
2aeb9b6d31
@ -208,6 +208,19 @@
|
||||
IsVisible="{Binding Cipher.ViewPassword}" />
|
||||
</Grid>
|
||||
|
||||
<Label
|
||||
Text="{u:I18n Passkey}"
|
||||
StyleClass="box-label"
|
||||
Margin="0,10,0,0"
|
||||
IsVisible="{Binding ShowPasskeyCreationDate}"/>
|
||||
<!--TODO: Update binding to display the true creation date of the login's Fido2Key.
|
||||
For now, displaying the creation date of the cipher to check the UI-->
|
||||
<Entry
|
||||
Text="{Binding CreationDate}"
|
||||
IsEnabled="False"
|
||||
StyleClass="box-value,text-muted"
|
||||
IsVisible="{Binding ShowPasskeyCreationDate}" />
|
||||
|
||||
<Grid StyleClass="box-row, box-row-input">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
|
@ -88,7 +88,6 @@ namespace Bit.App.Pages
|
||||
_watchDeviceService = ServiceContainer.Resolve<IWatchDeviceService>();
|
||||
_accountsManager = ServiceContainer.Resolve<IAccountsManager>();
|
||||
|
||||
|
||||
GeneratePasswordCommand = new Command(GeneratePassword);
|
||||
TogglePasswordCommand = new Command(TogglePassword);
|
||||
ToggleCardNumberCommand = new Command(ToggleCardNumber);
|
||||
@ -310,6 +309,7 @@ namespace Bit.App.Pages
|
||||
public string PasswordVisibilityAccessibilityText => ShowPassword ? AppResources.PasswordIsVisibleTapToHide : AppResources.PasswordIsNotVisibleTapToShow;
|
||||
public bool HasTotpValue => IsLogin && !string.IsNullOrEmpty(Cipher?.Login?.Totp);
|
||||
public string SetupTotpText => $"{BitwardenIcons.Camera} {AppResources.SetupTotp}";
|
||||
public bool ShowPasskeyCreationDate => Cipher?.Login?.Fido2Key != null && !CloneMode;
|
||||
|
||||
public void Init()
|
||||
{
|
||||
@ -368,6 +368,11 @@ namespace Bit.App.Pages
|
||||
{
|
||||
Cipher.OrganizationId = OrganizationId;
|
||||
}
|
||||
if (Cipher.Type == CipherType.Login)
|
||||
{
|
||||
// passkeys can't be cloned
|
||||
Cipher.Login.Fido2Key = null;
|
||||
}
|
||||
}
|
||||
if (appOptions?.OtpData != null && Cipher.Type == CipherType.Login)
|
||||
{
|
||||
|
@ -45,7 +45,7 @@
|
||||
x:Name="_attachmentsItem" x:Key="attachmentsItem" />
|
||||
<ToolbarItem Text="{u:I18n Delete}" Clicked="Delete_Clicked" Order="Secondary" IsDestructive="True"
|
||||
x:Name="_deleteItem" x:Key="deleteItem" />
|
||||
<ToolbarItem Text="{u:I18n Clone}" Clicked="Clone_Clicked" Order="Secondary"
|
||||
<ToolbarItem Text="{u:I18n Clone}" Command="{Binding CloneCommand}" Order="Secondary"
|
||||
x:Name="_cloneItem" x:Key="cloneItem" />
|
||||
|
||||
<DataTemplate x:Key="TextCustomFieldDataTemplate">
|
||||
@ -182,6 +182,18 @@
|
||||
</Grid>
|
||||
<BoxView StyleClass="box-row-separator"
|
||||
IsVisible="{Binding Cipher.Login.Password, Converter={StaticResource stringHasValue}}" />
|
||||
<Label
|
||||
Text="{u:I18n Passkey}"
|
||||
StyleClass="box-label"
|
||||
Margin="0,10,0,0"
|
||||
IsVisible="{Binding Cipher.Login.Fido2Key}"/>
|
||||
<!--TODO: Update binding to display the true creation date of the login's Fido2Key.
|
||||
For now, displaying the creation date of the cipher to check the UI-->
|
||||
<Entry
|
||||
Text="{Binding CreationDate}"
|
||||
IsEnabled="False"
|
||||
StyleClass="box-value,text-muted"
|
||||
IsVisible="{Binding Cipher.Login.Fido2Key}" />
|
||||
<Grid StyleClass="box-row" IsVisible="{Binding ShowTotp}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
|
@ -204,19 +204,6 @@ namespace Bit.App.Pages
|
||||
}
|
||||
}
|
||||
|
||||
private async void Clone_Clicked(object sender, System.EventArgs e)
|
||||
{
|
||||
if (DoOnce())
|
||||
{
|
||||
if (!await _vm.PromptPasswordAsync())
|
||||
{
|
||||
return;
|
||||
}
|
||||
var page = new CipherAddEditPage(_vm.CipherId, cloneMode: true, cipherDetailsPage: this);
|
||||
await Navigation.PushModalAsync(new NavigationPage(page));
|
||||
}
|
||||
}
|
||||
|
||||
private async void More_Clicked(object sender, System.EventArgs e)
|
||||
{
|
||||
if (!DoOnce())
|
||||
@ -267,8 +254,7 @@ namespace Bit.App.Pages
|
||||
}
|
||||
else if (selection == AppResources.Clone)
|
||||
{
|
||||
var page = new CipherAddEditPage(_vm.CipherId, cloneMode: true, cipherDetailsPage: this);
|
||||
await Navigation.PushModalAsync(new NavigationPage(page));
|
||||
_vm.CloneCommand.Execute(null);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -69,6 +69,7 @@ namespace Bit.App.Pages
|
||||
CopyUriCommand = new AsyncCommand<LoginUriView>(uriView => CopyAsync("LoginUri", uriView.Uri), onException: ex => _logger.Exception(ex), allowsMultipleExecutions: false);
|
||||
CopyFieldCommand = new AsyncCommand<FieldView>(field => CopyAsync(field.Type == FieldType.Hidden ? "H_FieldValue" : "FieldValue", field.Value), onException: ex => _logger.Exception(ex), allowsMultipleExecutions: false);
|
||||
LaunchUriCommand = new Command<ILaunchableView>(LaunchUri);
|
||||
CloneCommand = new AsyncCommand(CloneAsync, onException: ex => HandleException(ex), allowsMultipleExecutions: false);
|
||||
TogglePasswordCommand = new Command(TogglePassword);
|
||||
ToggleCardNumberCommand = new Command(ToggleCardNumber);
|
||||
ToggleCardCodeCommand = new Command(ToggleCardCode);
|
||||
@ -81,6 +82,7 @@ namespace Bit.App.Pages
|
||||
public ICommand CopyUriCommand { get; set; }
|
||||
public ICommand CopyFieldCommand { get; set; }
|
||||
public Command LaunchUriCommand { get; set; }
|
||||
public ICommand CloneCommand { get; set; }
|
||||
public Command TogglePasswordCommand { get; set; }
|
||||
public Command ToggleCardNumberCommand { get; set; }
|
||||
public Command ToggleCardCodeCommand { get; set; }
|
||||
@ -677,6 +679,17 @@ namespace Bit.App.Pages
|
||||
}
|
||||
}
|
||||
|
||||
private async Task CloneAsync()
|
||||
{
|
||||
if (!await CanCloneAsync() || !await PromptPasswordAsync())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var page = new CipherAddEditPage(CipherId, cloneMode: true, cipherDetailsPage: Page as CipherDetailsPage);
|
||||
await Page.Navigation.PushModalAsync(new NavigationPage(page));
|
||||
}
|
||||
|
||||
public async Task<bool> PromptPasswordAsync()
|
||||
{
|
||||
if (Cipher.Reprompt == CipherRepromptType.None || _passwordReprompted)
|
||||
@ -686,5 +699,15 @@ namespace Bit.App.Pages
|
||||
|
||||
return _passwordReprompted = await _passwordRepromptService.ShowPasswordPromptAsync();
|
||||
}
|
||||
|
||||
private async Task<bool> CanCloneAsync()
|
||||
{
|
||||
if (Cipher.Type == CipherType.Login && Cipher.Login?.Fido2Key != null)
|
||||
{
|
||||
return await _platformUtilsService.ShowDialogAsync(AppResources.ThePasskeyWillNotBeCopiedToTheClonedItemDoYouWantToContinueCloningThisItem, AppResources.PasskeyWillNotBeCopied, AppResources.Yes, AppResources.No);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
18
src/App/Resources/AppResources.Designer.cs
generated
18
src/App/Resources/AppResources.Designer.cs
generated
@ -4696,6 +4696,15 @@ namespace Bit.App.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Passkey will not be copied.
|
||||
/// </summary>
|
||||
public static string PasskeyWillNotBeCopied {
|
||||
get {
|
||||
return ResourceManager.GetString("PasskeyWillNotBeCopied", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Passphrase.
|
||||
/// </summary>
|
||||
@ -6092,6 +6101,15 @@ namespace Bit.App.Resources {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to The passkey will not be copied to the cloned item. Do you want to continue cloning this item?.
|
||||
/// </summary>
|
||||
public static string ThePasskeyWillNotBeCopiedToTheClonedItemDoYouWantToContinueCloningThisItem {
|
||||
get {
|
||||
return ResourceManager.GetString("ThePasskeyWillNotBeCopiedToTheClonedItemDoYouWantToContinueCloningThisItem", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to There are no items in your vault that match "{0}".
|
||||
/// </summary>
|
||||
|
@ -2632,4 +2632,10 @@ Do you want to switch to this account?</value>
|
||||
<data name="YouCannotEditPasskeyApplicationBecauseItWouldInvalidateThePasskey" xml:space="preserve">
|
||||
<value>You cannot edit passkey application because it would invalidate the passkey</value>
|
||||
</data>
|
||||
<data name="PasskeyWillNotBeCopied" xml:space="preserve">
|
||||
<value>Passkey will not be copied</value>
|
||||
</data>
|
||||
<data name="ThePasskeyWillNotBeCopiedToTheClonedItemDoYouWantToContinueCloningThisItem" xml:space="preserve">
|
||||
<value>The passkey will not be copied to the cloned item. Do you want to continue cloning this item?</value>
|
||||
</data>
|
||||
</root>
|
||||
|
@ -1,7 +1,28 @@
|
||||
namespace Bit.Core.Models.Api
|
||||
using Bit.Core.Models.Domain;
|
||||
using Bit.Core.Models.Export;
|
||||
|
||||
namespace Bit.Core.Models.Api
|
||||
{
|
||||
public class Fido2KeyApi
|
||||
{
|
||||
public Fido2KeyApi()
|
||||
{
|
||||
}
|
||||
|
||||
public Fido2KeyApi(Fido2Key fido2Key)
|
||||
{
|
||||
NonDiscoverableId = fido2Key.NonDiscoverableId?.EncryptedString;
|
||||
KeyType = fido2Key.KeyType?.EncryptedString;
|
||||
KeyAlgorithm = fido2Key.KeyAlgorithm?.EncryptedString;
|
||||
KeyCurve = fido2Key.KeyCurve?.EncryptedString;
|
||||
KeyValue = fido2Key.KeyValue?.EncryptedString;
|
||||
RpId = fido2Key.RpId?.EncryptedString;
|
||||
RpName = fido2Key.RpName?.EncryptedString;
|
||||
UserHandle = fido2Key.UserHandle?.EncryptedString;
|
||||
UserName = fido2Key.UserName?.EncryptedString;
|
||||
Counter = fido2Key.Counter?.EncryptedString;
|
||||
}
|
||||
|
||||
public string NonDiscoverableId { get; set; }
|
||||
public string KeyType { get; set; } = Constants.DefaultFido2KeyType;
|
||||
public string KeyAlgorithm { get; set; } = Constants.DefaultFido2KeyAlgorithm;
|
||||
|
@ -15,6 +15,7 @@ namespace Bit.Core.Models.Domain
|
||||
{
|
||||
PasswordRevisionDate = obj.PasswordRevisionDate;
|
||||
Uris = obj.Uris?.Select(u => new LoginUri(u, alreadyEncrypted)).ToList();
|
||||
Fido2Key = obj.Fido2Key != null ? new Fido2Key(obj.Fido2Key, alreadyEncrypted) : null;
|
||||
BuildDomainModel(this, obj, new HashSet<string>
|
||||
{
|
||||
"Username",
|
||||
|
@ -30,7 +30,8 @@ namespace Bit.Core.Models.Request
|
||||
Username = cipher.Login.Username?.EncryptedString,
|
||||
Password = cipher.Login.Password?.EncryptedString,
|
||||
PasswordRevisionDate = cipher.Login.PasswordRevisionDate,
|
||||
Totp = cipher.Login.Totp?.EncryptedString
|
||||
Totp = cipher.Login.Totp?.EncryptedString,
|
||||
Fido2Key = cipher.Login.Fido2Key != null ? new Fido2KeyApi(cipher.Login.Fido2Key) : null
|
||||
};
|
||||
break;
|
||||
case CipherType.Card:
|
||||
@ -74,19 +75,7 @@ namespace Bit.Core.Models.Request
|
||||
};
|
||||
break;
|
||||
case CipherType.Fido2Key:
|
||||
Fido2Key = new Fido2KeyApi
|
||||
{
|
||||
NonDiscoverableId = cipher.Fido2Key.NonDiscoverableId?.EncryptedString,
|
||||
KeyType = cipher.Fido2Key.KeyType?.EncryptedString,
|
||||
KeyAlgorithm = cipher.Fido2Key.KeyAlgorithm?.EncryptedString,
|
||||
KeyCurve = cipher.Fido2Key.KeyCurve?.EncryptedString,
|
||||
KeyValue = cipher.Fido2Key.KeyValue?.EncryptedString,
|
||||
RpId = cipher.Fido2Key.RpId?.EncryptedString,
|
||||
RpName = cipher.Fido2Key.RpName?.EncryptedString,
|
||||
UserHandle = cipher.Fido2Key.UserHandle?.EncryptedString,
|
||||
UserName = cipher.Fido2Key.UserName?.EncryptedString,
|
||||
Counter = cipher.Fido2Key.Counter?.EncryptedString
|
||||
};
|
||||
Fido2Key = new Fido2KeyApi(cipher.Fido2Key);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
Loading…
Reference in New Issue
Block a user