Skip to content

Commit 112049b

Browse files
authored
[PM-18466] Add support for Fido2 to cipher list view (#178)
## 🎟️ Tracking <!-- Paste the link to the Jira or GitHub issue or otherwise describe / point to where this change is coming from. --> ## 📔 Objective Allows mobile apps to start using `CipherListView` ## ⏰ Reminders before review - Contributor guidelines followed - All formatters and local linters executed and passed - Written new unit and / or integration tests where applicable - Protected functional changes with optionality (feature flags) - Used internationalization (i18n) for all UI strings - CI builds passed - Communicated to DevOps any deployment requirements - Updated any necessary documentation (Confluence, contributing docs) or informed the documentation team ## 🦮 Reviewer guidelines <!-- Suggested interactions but feel free to use (or not) as you desire! --> - 👍 (`:+1:`) or similar for great changes - 📝 (`:memo:`) or ℹ️ (`:information_source:`) for notes or general info - ❓ (`:question:`) for questions - 🤔 (`:thinking:`) or 💭 (`:thought_balloon:`) for more open inquiry that's not quite a confirmed issue and could potentially benefit from discussion - 🎨 (`:art:`) for suggestions / improvements - ❌ (`:x:`) or ⚠️ (`:warning:`) for more significant problems or concerns needing attention - 🌱 (`:seedling:`) or ♻️ (`:recycle:`) for future improvements or indications of technical debt - ⛏ (`:pick:`) for minor or nitpick changes
1 parent 591c93a commit 112049b

File tree

9 files changed

+99
-19
lines changed

9 files changed

+99
-19
lines changed

crates/bitwarden-fido/src/authenticator.rs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -269,17 +269,13 @@ impl<'a> Fido2Authenticator<'a> {
269269
pub async fn credentials_for_autofill(
270270
&mut self,
271271
) -> Result<Vec<Fido2CredentialAutofillView>, CredentialsForAutofillError> {
272-
let key_store = self.client.internal.get_key_store();
273272
let all_credentials = self.credential_store.all_credentials().await?;
274273

275-
let mut ctx = key_store.context();
276274
all_credentials
277275
.into_iter()
278276
.map(
279277
|cipher| -> Result<Vec<Fido2CredentialAutofillView>, CredentialsForAutofillError> {
280-
Ok(Fido2CredentialAutofillView::from_cipher_view(
281-
&cipher, &mut ctx,
282-
)?)
278+
Ok(Fido2CredentialAutofillView::from_cipher_list_view(&cipher)?)
283279
},
284280
)
285281
.flatten_ok()

crates/bitwarden-fido/src/traits.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use bitwarden_vault::{Cipher, CipherView, Fido2CredentialNewView};
1+
use bitwarden_vault::{Cipher, CipherListView, CipherView, Fido2CredentialNewView};
22
use passkey::authenticator::UIHint;
33
use thiserror::Error;
44

@@ -41,7 +41,7 @@ pub trait Fido2CredentialStore: Send + Sync {
4141
rip_id: String,
4242
) -> Result<Vec<CipherView>, Fido2CallbackError>;
4343

44-
async fn all_credentials(&self) -> Result<Vec<CipherView>, Fido2CallbackError>;
44+
async fn all_credentials(&self) -> Result<Vec<CipherListView>, Fido2CallbackError>;
4545

4646
async fn save_credential(&self, cred: Cipher) -> Result<(), Fido2CallbackError>;
4747
}

crates/bitwarden-fido/src/types.rs

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use std::borrow::Cow;
33
use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine};
44
use bitwarden_core::key_management::KeyIds;
55
use bitwarden_crypto::{CryptoError, KeyStoreContext};
6-
use bitwarden_vault::CipherView;
6+
use bitwarden_vault::{CipherListView, CipherListViewType, CipherView, LoginListView};
77
use passkey::types::webauthn::UserVerificationRequirement;
88
use reqwest::Url;
99
use schemars::JsonSchema;
@@ -71,9 +71,10 @@ impl Fido2CredentialAutofillView {
7171
let credentials = cipher.decrypt_fido2_credentials(ctx)?;
7272

7373
credentials
74-
.into_iter()
74+
.iter()
7575
.filter_map(|c| -> Option<Result<_, Fido2CredentialAutofillViewError>> {
7676
c.user_handle
77+
.as_ref()
7778
.map(|u| URL_SAFE_NO_PAD.decode(u))
7879
.map(|user_handle| {
7980
Ok(Fido2CredentialAutofillView {
@@ -97,6 +98,42 @@ impl Fido2CredentialAutofillView {
9798
})
9899
.collect()
99100
}
101+
102+
pub fn from_cipher_list_view(
103+
cipher: &CipherListView,
104+
) -> Result<Vec<Fido2CredentialAutofillView>, Fido2CredentialAutofillViewError> {
105+
match &cipher.r#type {
106+
CipherListViewType::Login(LoginListView {
107+
fido2_credentials: Some(fido2_credentials),
108+
username,
109+
..
110+
}) => fido2_credentials
111+
.iter()
112+
.filter_map(|c| -> Option<Result<_, Fido2CredentialAutofillViewError>> {
113+
c.user_handle
114+
.as_ref()
115+
.map(|u| URL_SAFE_NO_PAD.decode(u))
116+
.map(|user_handle| {
117+
Ok(Fido2CredentialAutofillView {
118+
credential_id: string_to_guid_bytes(&c.credential_id)?,
119+
cipher_id: cipher
120+
.id
121+
.ok_or(Fido2CredentialAutofillViewError::MissingCipherId)?,
122+
rp_id: c.rp_id.clone(),
123+
user_handle: user_handle?,
124+
user_name_for_ui: c
125+
.user_name
126+
.none_whitespace()
127+
.or(c.user_display_name.none_whitespace())
128+
.or(username.none_whitespace())
129+
.or(cipher.name.none_whitespace()),
130+
})
131+
})
132+
})
133+
.collect(),
134+
_ => Ok(vec![]),
135+
}
136+
}
100137
}
101138

102139
#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]

crates/bitwarden-uniffi/src/platform/fido2.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use bitwarden_fido::{
77
PublicKeyCredentialAuthenticatorAttestationResponse, PublicKeyCredentialRpEntity,
88
PublicKeyCredentialUserEntity,
99
};
10-
use bitwarden_vault::{Cipher, CipherView, Fido2CredentialNewView};
10+
use bitwarden_vault::{Cipher, CipherListView, CipherView, Fido2CredentialNewView};
1111

1212
use crate::{
1313
error::{Error, Result},
@@ -255,7 +255,7 @@ pub trait Fido2CredentialStore: Send + Sync {
255255
rip_id: String,
256256
) -> Result<Vec<CipherView>, Fido2CallbackError>;
257257

258-
async fn all_credentials(&self) -> Result<Vec<CipherView>, Fido2CallbackError>;
258+
async fn all_credentials(&self) -> Result<Vec<CipherListView>, Fido2CallbackError>;
259259

260260
async fn save_credential(&self, cred: Cipher) -> Result<(), Fido2CallbackError>;
261261
}
@@ -279,7 +279,7 @@ impl bitwarden_fido::Fido2CredentialStore for UniffiTraitBridge<&dyn Fido2Creden
279279
.map_err(Into::into)
280280
}
281281

282-
async fn all_credentials(&self) -> Result<Vec<CipherView>, BitFido2CallbackError> {
282+
async fn all_credentials(&self) -> Result<Vec<CipherListView>, BitFido2CallbackError> {
283283
self.0.all_credentials().await.map_err(Into::into)
284284
}
285285

crates/bitwarden-uniffi/swift/iOS/App/ContentView.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -402,17 +402,17 @@ class Fido2UserInterfaceImpl: Fido2UserInterface {
402402
func checkUser(options: BitwardenSdk.CheckUserOptions, hint: UiHint) async throws -> BitwardenSdk.CheckUserResult {
403403
return CheckUserResult(userPresent: true, userVerified: true)
404404
}
405-
405+
406406
func isVerificationEnabled() async -> Bool {
407407
true
408408
}
409409
}
410410

411411
class Fido2CredentialStoreImpl: Fido2CredentialStore {
412-
func allCredentials() async throws -> [BitwardenSdk.CipherView] {
412+
func allCredentials() async throws -> [BitwardenSdk.CipherListView] {
413413
abort()
414414
}
415-
415+
416416
func findCredentials(ids: [Data]?, ripId: String) async throws -> [BitwardenSdk.CipherView] {
417417
abort()
418418
}

crates/bitwarden-vault/src/cipher/cipher.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -734,7 +734,7 @@ mod tests {
734734
use ssh_key::SshKey;
735735

736736
use super::*;
737-
use crate::Fido2Credential;
737+
use crate::{login::Fido2CredentialListView, Fido2Credential};
738738

739739
fn generate_cipher() -> CipherView {
740740
let test_id: uuid::Uuid = "fd411a1a-fec8-4070-985d-0e6560860e69".parse().unwrap();
@@ -852,7 +852,15 @@ mod tests {
852852
name: "My test login".to_string(),
853853
subtitle: "test_username".to_string(),
854854
r#type: CipherListViewType::Login(LoginListView {
855+
fido2_credentials: Some(vec![Fido2CredentialListView {
856+
credential_id: "123".to_string(),
857+
rp_id: "123".to_string(),
858+
user_handle: None,
859+
user_name: None,
860+
user_display_name: None,
861+
}]),
855862
has_fido2: true,
863+
username: Some("test_username".to_string()),
856864
totp: cipher.login.as_ref().unwrap().totp.clone(),
857865
uris: None,
858866
}),

crates/bitwarden-vault/src/cipher/login.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,17 @@ pub struct Fido2Credential {
9090
pub creation_date: DateTime<Utc>,
9191
}
9292

93+
#[derive(Serialize, Deserialize, Debug, JsonSchema, Clone, PartialEq)]
94+
#[serde(rename_all = "camelCase", deny_unknown_fields)]
95+
#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
96+
pub struct Fido2CredentialListView {
97+
pub credential_id: String,
98+
pub rp_id: String,
99+
pub user_handle: Option<String>,
100+
pub user_name: Option<String>,
101+
pub user_display_name: Option<String>,
102+
}
103+
93104
#[derive(Serialize, Deserialize, Debug, JsonSchema, Clone)]
94105
#[serde(rename_all = "camelCase", deny_unknown_fields)]
95106
#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
@@ -280,7 +291,9 @@ pub struct LoginView {
280291
#[serde(rename_all = "camelCase", deny_unknown_fields)]
281292
#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
282293
pub struct LoginListView {
294+
pub fido2_credentials: Option<Vec<Fido2CredentialListView>>,
283295
pub has_fido2: bool,
296+
pub username: Option<String>,
284297
/// The TOTP key is not decrypted. Useable as is with [`crate::generate_totp_cipher_view`].
285298
pub totp: Option<EncString>,
286299
pub uris: Option<Vec<LoginUriView>>,
@@ -357,7 +370,13 @@ impl Decryptable<KeyIds, SymmetricKeyId, LoginListView> for Login {
357370
key: SymmetricKeyId,
358371
) -> Result<LoginListView, CryptoError> {
359372
Ok(LoginListView {
373+
fido2_credentials: self
374+
.fido2_credentials
375+
.as_ref()
376+
.map(|fido2_credentials| fido2_credentials.decrypt(ctx, key))
377+
.transpose()?,
360378
has_fido2: self.fido2_credentials.is_some(),
379+
username: self.username.decrypt(ctx, key).ok().flatten(),
361380
totp: self.totp.clone(),
362381
uris: self.uris.decrypt(ctx, key).ok().flatten(),
363382
})
@@ -420,6 +439,22 @@ impl Decryptable<KeyIds, SymmetricKeyId, Fido2CredentialView> for Fido2Credentia
420439
}
421440
}
422441

442+
impl Decryptable<KeyIds, SymmetricKeyId, Fido2CredentialListView> for Fido2Credential {
443+
fn decrypt(
444+
&self,
445+
ctx: &mut KeyStoreContext<KeyIds>,
446+
key: SymmetricKeyId,
447+
) -> Result<Fido2CredentialListView, CryptoError> {
448+
Ok(Fido2CredentialListView {
449+
credential_id: self.credential_id.decrypt(ctx, key)?,
450+
rp_id: self.rp_id.decrypt(ctx, key)?,
451+
user_handle: self.user_handle.decrypt(ctx, key)?,
452+
user_name: self.user_name.decrypt(ctx, key)?,
453+
user_display_name: self.user_display_name.decrypt(ctx, key)?,
454+
})
455+
}
456+
}
457+
423458
impl TryFrom<CipherLoginModel> for Login {
424459
type Error = VaultParseError;
425460

crates/bitwarden-vault/src/cipher/mod.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,15 @@ pub use attachment::{
1515
Attachment, AttachmentEncryptResult, AttachmentFile, AttachmentFileView, AttachmentView,
1616
};
1717
pub use card::{CardBrand, CardView};
18-
pub use cipher::{Cipher, CipherError, CipherListView, CipherRepromptType, CipherType, CipherView};
19-
pub use cipher_permissions::CipherPermissions;
18+
pub use cipher::{
19+
Cipher, CipherError, CipherListView, CipherListViewType, CipherRepromptType, CipherType,
20+
CipherView,
21+
};
2022
pub use field::FieldView;
2123
pub use identity::IdentityView;
2224
pub use login::{
2325
Fido2Credential, Fido2CredentialFullView, Fido2CredentialNewView, Fido2CredentialView, Login,
24-
LoginUriView, LoginView, UriMatchType,
26+
LoginListView, LoginUriView, LoginView, UriMatchType,
2527
};
2628
pub use secure_note::{SecureNoteType, SecureNoteView};
2729
pub use ssh_key::SshKeyView;

crates/bitwarden-vault/src/totp.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -727,7 +727,9 @@ mod tests {
727727
name: "My test login".to_string(),
728728
subtitle: "test_username".to_string(),
729729
r#type: CipherListViewType::Login(LoginListView{
730+
fido2_credentials: None,
730731
has_fido2: true,
732+
username: None,
731733
totp: Some("2.hqdioUAc81FsKQmO1XuLQg==|oDRdsJrQjoFu9NrFVy8tcJBAFKBx95gHaXZnWdXbKpsxWnOr2sKipIG43pKKUFuq|3gKZMiboceIB5SLVOULKg2iuyu6xzos22dfJbvx0EHk=".parse().unwrap()),
732734
uris: None,
733735
}),

0 commit comments

Comments
 (0)