mirror of
https://github.com/bitwarden/mobile.git
synced 2025-01-24 21:31:31 +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}" />
|
IsVisible="{Binding Cipher.ViewPassword}" />
|
||||||
</Grid>
|
</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 StyleClass="box-row, box-row-input">
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
|
@ -88,7 +88,6 @@ namespace Bit.App.Pages
|
|||||||
_watchDeviceService = ServiceContainer.Resolve<IWatchDeviceService>();
|
_watchDeviceService = ServiceContainer.Resolve<IWatchDeviceService>();
|
||||||
_accountsManager = ServiceContainer.Resolve<IAccountsManager>();
|
_accountsManager = ServiceContainer.Resolve<IAccountsManager>();
|
||||||
|
|
||||||
|
|
||||||
GeneratePasswordCommand = new Command(GeneratePassword);
|
GeneratePasswordCommand = new Command(GeneratePassword);
|
||||||
TogglePasswordCommand = new Command(TogglePassword);
|
TogglePasswordCommand = new Command(TogglePassword);
|
||||||
ToggleCardNumberCommand = new Command(ToggleCardNumber);
|
ToggleCardNumberCommand = new Command(ToggleCardNumber);
|
||||||
@ -310,6 +309,7 @@ namespace Bit.App.Pages
|
|||||||
public string PasswordVisibilityAccessibilityText => ShowPassword ? AppResources.PasswordIsVisibleTapToHide : AppResources.PasswordIsNotVisibleTapToShow;
|
public string PasswordVisibilityAccessibilityText => ShowPassword ? AppResources.PasswordIsVisibleTapToHide : AppResources.PasswordIsNotVisibleTapToShow;
|
||||||
public bool HasTotpValue => IsLogin && !string.IsNullOrEmpty(Cipher?.Login?.Totp);
|
public bool HasTotpValue => IsLogin && !string.IsNullOrEmpty(Cipher?.Login?.Totp);
|
||||||
public string SetupTotpText => $"{BitwardenIcons.Camera} {AppResources.SetupTotp}";
|
public string SetupTotpText => $"{BitwardenIcons.Camera} {AppResources.SetupTotp}";
|
||||||
|
public bool ShowPasskeyCreationDate => Cipher?.Login?.Fido2Key != null && !CloneMode;
|
||||||
|
|
||||||
public void Init()
|
public void Init()
|
||||||
{
|
{
|
||||||
@ -368,6 +368,11 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
Cipher.OrganizationId = OrganizationId;
|
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)
|
if (appOptions?.OtpData != null && Cipher.Type == CipherType.Login)
|
||||||
{
|
{
|
||||||
|
@ -45,7 +45,7 @@
|
|||||||
x:Name="_attachmentsItem" x:Key="attachmentsItem" />
|
x:Name="_attachmentsItem" x:Key="attachmentsItem" />
|
||||||
<ToolbarItem Text="{u:I18n Delete}" Clicked="Delete_Clicked" Order="Secondary" IsDestructive="True"
|
<ToolbarItem Text="{u:I18n Delete}" Clicked="Delete_Clicked" Order="Secondary" IsDestructive="True"
|
||||||
x:Name="_deleteItem" x:Key="deleteItem" />
|
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" />
|
x:Name="_cloneItem" x:Key="cloneItem" />
|
||||||
|
|
||||||
<DataTemplate x:Key="TextCustomFieldDataTemplate">
|
<DataTemplate x:Key="TextCustomFieldDataTemplate">
|
||||||
@ -182,6 +182,18 @@
|
|||||||
</Grid>
|
</Grid>
|
||||||
<BoxView StyleClass="box-row-separator"
|
<BoxView StyleClass="box-row-separator"
|
||||||
IsVisible="{Binding Cipher.Login.Password, Converter={StaticResource stringHasValue}}" />
|
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 StyleClass="box-row" IsVisible="{Binding ShowTotp}">
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="Auto" />
|
<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)
|
private async void More_Clicked(object sender, System.EventArgs e)
|
||||||
{
|
{
|
||||||
if (!DoOnce())
|
if (!DoOnce())
|
||||||
@ -267,8 +254,7 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
else if (selection == AppResources.Clone)
|
else if (selection == AppResources.Clone)
|
||||||
{
|
{
|
||||||
var page = new CipherAddEditPage(_vm.CipherId, cloneMode: true, cipherDetailsPage: this);
|
_vm.CloneCommand.Execute(null);
|
||||||
await Navigation.PushModalAsync(new NavigationPage(page));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,6 +69,7 @@ namespace Bit.App.Pages
|
|||||||
CopyUriCommand = new AsyncCommand<LoginUriView>(uriView => CopyAsync("LoginUri", uriView.Uri), onException: ex => _logger.Exception(ex), allowsMultipleExecutions: false);
|
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);
|
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);
|
LaunchUriCommand = new Command<ILaunchableView>(LaunchUri);
|
||||||
|
CloneCommand = new AsyncCommand(CloneAsync, onException: ex => HandleException(ex), allowsMultipleExecutions: false);
|
||||||
TogglePasswordCommand = new Command(TogglePassword);
|
TogglePasswordCommand = new Command(TogglePassword);
|
||||||
ToggleCardNumberCommand = new Command(ToggleCardNumber);
|
ToggleCardNumberCommand = new Command(ToggleCardNumber);
|
||||||
ToggleCardCodeCommand = new Command(ToggleCardCode);
|
ToggleCardCodeCommand = new Command(ToggleCardCode);
|
||||||
@ -81,6 +82,7 @@ namespace Bit.App.Pages
|
|||||||
public ICommand CopyUriCommand { get; set; }
|
public ICommand CopyUriCommand { get; set; }
|
||||||
public ICommand CopyFieldCommand { get; set; }
|
public ICommand CopyFieldCommand { get; set; }
|
||||||
public Command LaunchUriCommand { get; set; }
|
public Command LaunchUriCommand { get; set; }
|
||||||
|
public ICommand CloneCommand { get; set; }
|
||||||
public Command TogglePasswordCommand { get; set; }
|
public Command TogglePasswordCommand { get; set; }
|
||||||
public Command ToggleCardNumberCommand { get; set; }
|
public Command ToggleCardNumberCommand { get; set; }
|
||||||
public Command ToggleCardCodeCommand { 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()
|
public async Task<bool> PromptPasswordAsync()
|
||||||
{
|
{
|
||||||
if (Cipher.Reprompt == CipherRepromptType.None || _passwordReprompted)
|
if (Cipher.Reprompt == CipherRepromptType.None || _passwordReprompted)
|
||||||
@ -686,5 +699,15 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
return _passwordReprompted = await _passwordRepromptService.ShowPasswordPromptAsync();
|
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>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Passphrase.
|
/// Looks up a localized string similar to Passphrase.
|
||||||
/// </summary>
|
/// </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>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to There are no items in your vault that match "{0}".
|
/// Looks up a localized string similar to There are no items in your vault that match "{0}".
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -2632,4 +2632,10 @@ Do you want to switch to this account?</value>
|
|||||||
<data name="YouCannotEditPasskeyApplicationBecauseItWouldInvalidateThePasskey" xml:space="preserve">
|
<data name="YouCannotEditPasskeyApplicationBecauseItWouldInvalidateThePasskey" xml:space="preserve">
|
||||||
<value>You cannot edit passkey application because it would invalidate the passkey</value>
|
<value>You cannot edit passkey application because it would invalidate the passkey</value>
|
||||||
</data>
|
</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>
|
</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 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 NonDiscoverableId { get; set; }
|
||||||
public string KeyType { get; set; } = Constants.DefaultFido2KeyType;
|
public string KeyType { get; set; } = Constants.DefaultFido2KeyType;
|
||||||
public string KeyAlgorithm { get; set; } = Constants.DefaultFido2KeyAlgorithm;
|
public string KeyAlgorithm { get; set; } = Constants.DefaultFido2KeyAlgorithm;
|
||||||
|
@ -15,6 +15,7 @@ namespace Bit.Core.Models.Domain
|
|||||||
{
|
{
|
||||||
PasswordRevisionDate = obj.PasswordRevisionDate;
|
PasswordRevisionDate = obj.PasswordRevisionDate;
|
||||||
Uris = obj.Uris?.Select(u => new LoginUri(u, alreadyEncrypted)).ToList();
|
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>
|
BuildDomainModel(this, obj, new HashSet<string>
|
||||||
{
|
{
|
||||||
"Username",
|
"Username",
|
||||||
|
@ -30,7 +30,8 @@ namespace Bit.Core.Models.Request
|
|||||||
Username = cipher.Login.Username?.EncryptedString,
|
Username = cipher.Login.Username?.EncryptedString,
|
||||||
Password = cipher.Login.Password?.EncryptedString,
|
Password = cipher.Login.Password?.EncryptedString,
|
||||||
PasswordRevisionDate = cipher.Login.PasswordRevisionDate,
|
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;
|
break;
|
||||||
case CipherType.Card:
|
case CipherType.Card:
|
||||||
@ -74,19 +75,7 @@ namespace Bit.Core.Models.Request
|
|||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
case CipherType.Fido2Key:
|
case CipherType.Fido2Key:
|
||||||
Fido2Key = new Fido2KeyApi
|
Fido2Key = new Fido2KeyApi(cipher.Fido2Key);
|
||||||
{
|
|
||||||
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
|
|
||||||
};
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
Loading…
Reference in New Issue
Block a user