From 712c95afe10eda175a8dfb3e78ebba979cc145ee Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Tue, 2 Apr 2024 16:21:13 +0200 Subject: [PATCH 1/3] [PM-7145] feat: set UV depending on options --- passkey-client/src/lib.rs | 17 +++++-- passkey-client/src/tests/mod.rs | 83 +++++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+), 3 deletions(-) diff --git a/passkey-client/src/lib.rs b/passkey-client/src/lib.rs index 1b86b2f..3e3d7ea 100644 --- a/passkey-client/src/lib.rs +++ b/passkey-client/src/lib.rs @@ -21,8 +21,11 @@ use coset::{iana::EnumI64, Algorithm}; use passkey_authenticator::{Authenticator, CredentialStore, UserValidationMethod}; use passkey_types::{ crypto::sha256, - ctap2, encoding, webauthn, - webauthn::{AuthenticatorExtensionsClientOutputs, CredentialPropertiesOutput}, + ctap2, encoding, + webauthn::{ + self, AuthenticatorExtensionsClientOutputs, CredentialPropertiesOutput, + UserVerificationRequirement, + }, Passkey, }; use typeshare::typeshare; @@ -209,6 +212,14 @@ where None }; + let uv = matches!( + request + .authenticator_selection + .map(|s| s.user_verification) + .unwrap_or_default(), // Default is Preferred + UserVerificationRequirement::Required | UserVerificationRequirement::Preferred + ); + let ctap2_response = self .authenticator .make_credential(ctap2::make_credential::Request { @@ -224,7 +235,7 @@ where options: ctap2::make_credential::Options { rk: true, up: true, - uv: true, + uv, }, pin_auth: None, pin_protocol: None, diff --git a/passkey-client/src/tests/mod.rs b/passkey-client/src/tests/mod.rs index ff0dc31..aed09e6 100644 --- a/passkey-client/src/tests/mod.rs +++ b/passkey-client/src/tests/mod.rs @@ -287,3 +287,86 @@ fn validate_domain_with_private_list_provider() -> Result<(), ParseError> { Ok(()) } + +fn user_mock_with_uv() -> MockUserValidationMethod { + let mut user_mock = MockUserValidationMethod::new(); + user_mock + .expect_is_verification_enabled() + .returning(|| Some(true)); + user_mock + .expect_check_user_verification() + .returning(|| Box::pin(async { true })); + // Always called by `get_info` + user_mock + .expect_is_verification_enabled() + .returning(|| Some(true)); + user_mock.expect_is_presence_enabled().returning(|| true); + user_mock +} + +fn user_mock_without_uv() -> MockUserValidationMethod { + let mut user_mock = MockUserValidationMethod::new(); + user_mock + .expect_check_user_presence() + .returning(|| Box::pin(async { true })); + // Always called by `get_info` + user_mock + .expect_is_verification_enabled() + .returning(|| Some(true)); + user_mock.expect_is_presence_enabled().returning(|| true); + user_mock +} + +#[tokio::test] +async fn client_register_triggers_uv_when_uv_is_required() { + // Arrange + let auth = Authenticator::new( + ctap2::Aaguid::new_empty(), + MemoryStore::new(), + user_mock_with_uv(), + ); + let mut client = Client::new(auth); + let origin = Url::parse("https://future.1password.com").unwrap(); + let mut options = webauthn::CredentialCreationOptions { + public_key: good_credential_creation_options(), + }; + options.public_key.authenticator_selection = Some(webauthn::AuthenticatorSelectionCriteria { + user_verification: webauthn::UserVerificationRequirement::Required, + authenticator_attachment: Default::default(), + resident_key: Default::default(), + require_resident_key: Default::default(), + }); + + // Act & Assert + client + .register(&origin, options, None) + .await + .expect("failed to register with options"); +} + +#[tokio::test] +async fn client_register_does_not_trigger_uv_when_uv_is_discouraged() { + // Arrange + let auth = Authenticator::new( + ctap2::Aaguid::new_empty(), + MemoryStore::new(), + user_mock_without_uv(), + ); + let mut client = Client::new(auth); + let origin = Url::parse("https://future.1password.com").unwrap(); + let mut options = webauthn::CredentialCreationOptions { + public_key: good_credential_creation_options(), + }; + options.public_key.authenticator_selection = Some(webauthn::AuthenticatorSelectionCriteria { + user_verification: webauthn::UserVerificationRequirement::Discouraged, + authenticator_attachment: Default::default(), + resident_key: Default::default(), + require_resident_key: Default::default(), + }); + + // Act & Assert + client + .register(&origin, options, None) + .await + .expect("failed to register with options"); +} From 3ded704f0765d6fa0150e98a83cfc20ea05007e4 Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Wed, 3 Apr 2024 10:06:50 +0200 Subject: [PATCH 2/3] [PM-7145] fix: invert statement for clarity --- passkey-client/src/lib.rs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/passkey-client/src/lib.rs b/passkey-client/src/lib.rs index 3e3d7ea..0235418 100644 --- a/passkey-client/src/lib.rs +++ b/passkey-client/src/lib.rs @@ -212,13 +212,8 @@ where None }; - let uv = matches!( - request - .authenticator_selection - .map(|s| s.user_verification) - .unwrap_or_default(), // Default is Preferred - UserVerificationRequirement::Required | UserVerificationRequirement::Preferred - ); + let uv = request.authenticator_selection.map(|s| s.user_verification) + != Some(UserVerificationRequirement::Discouraged); let ctap2_response = self .authenticator From 64afac7ddeb6e5fa190ceef8ffce91a1b917eb9a Mon Sep 17 00:00:00 2001 From: Andreas Coroiu Date: Mon, 10 Jun 2024 14:14:28 +0200 Subject: [PATCH 3/3] [PM-8752] fix: unused qualifications --- passkey-client/src/tests/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/passkey-client/src/tests/mod.rs b/passkey-client/src/tests/mod.rs index aed09e6..2cfe70c 100644 --- a/passkey-client/src/tests/mod.rs +++ b/passkey-client/src/tests/mod.rs @@ -331,7 +331,7 @@ async fn client_register_triggers_uv_when_uv_is_required() { public_key: good_credential_creation_options(), }; options.public_key.authenticator_selection = Some(webauthn::AuthenticatorSelectionCriteria { - user_verification: webauthn::UserVerificationRequirement::Required, + user_verification: UserVerificationRequirement::Required, authenticator_attachment: Default::default(), resident_key: Default::default(), require_resident_key: Default::default(), @@ -358,7 +358,7 @@ async fn client_register_does_not_trigger_uv_when_uv_is_discouraged() { public_key: good_credential_creation_options(), }; options.public_key.authenticator_selection = Some(webauthn::AuthenticatorSelectionCriteria { - user_verification: webauthn::UserVerificationRequirement::Discouraged, + user_verification: UserVerificationRequirement::Discouraged, authenticator_attachment: Default::default(), resident_key: Default::default(), require_resident_key: Default::default(),