blob: f5bbb0191baf771bf9329b426cf4fcbf2fe53bc3 [file] [log] [blame]
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/autofill/core/browser/payments/credit_card_access_manager.h"
#include <stddef.h>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "base/base64.h"
#include "base/functional/bind.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/values.h"
#include "build/build_config.h"
#include "components/autofill/core/browser/data_manager/payments/payments_data_manager.h"
#include "components/autofill/core/browser/data_manager/test_personal_data_manager.h"
#include "components/autofill/core/browser/data_model/payments/credit_card.h"
#include "components/autofill/core/browser/form_import/form_data_importer_test_api.h"
#include "components/autofill/core/browser/foundations/test_autofill_client.h"
#include "components/autofill/core/browser/foundations/test_autofill_driver.h"
#include "components/autofill/core/browser/metrics/form_events/credit_card_form_event_logger.h"
#include "components/autofill/core/browser/metrics/payments/better_auth_metrics.h"
#include "components/autofill/core/browser/metrics/payments/card_unmask_authentication_metrics.h"
#include "components/autofill/core/browser/metrics/payments/card_unmask_flow_metrics.h"
#include "components/autofill/core/browser/payments/autofill_error_dialog_context.h"
#include "components/autofill/core/browser/payments/card_unmask_challenge_option.h"
#include "components/autofill/core/browser/payments/credit_card_access_manager_test_api.h"
#include "components/autofill/core/browser/payments/credit_card_access_manager_test_base.h"
#include "components/autofill/core/browser/payments/credit_card_cvc_authenticator.h"
#include "components/autofill/core/browser/payments/credit_card_risk_based_authenticator.h"
#include "components/autofill/core/browser/payments/payments_autofill_client.h"
#include "components/autofill/core/browser/payments/payments_window_manager.h"
#include "components/autofill/core/browser/payments/test/mock_payments_window_manager.h"
#include "components/autofill/core/browser/payments/test/mock_virtual_card_enrollment_manager.h"
#include "components/autofill/core/browser/payments/test/test_credit_card_otp_authenticator.h"
#include "components/autofill/core/browser/test_utils/autofill_test_utils.h"
#include "components/autofill/core/common/autofill_payments_features.h"
#include "components/autofill/core/common/autofill_prefs.h"
#include "testing/gtest/include/gtest/gtest.h"
#if BUILDFLAG(IS_ANDROID)
#include "base/android/device_info.h"
#endif
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_ANDROID)
#include "components/autofill/core/browser/payments/test_credit_card_fido_authenticator.h"
#include "components/autofill/core/browser/strike_databases/payments/fido_authentication_strike_database.h"
#endif
using base::ASCIIToUTF16;
using testing::NiceMock;
namespace autofill {
namespace {
using PaymentsRpcCardType =
payments::PaymentsAutofillClient::PaymentsRpcCardType;
using PaymentsRpcResult = payments::PaymentsAutofillClient::PaymentsRpcResult;
using autofill_metrics::CreditCardFormEventLogger;
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_ANDROID)
std::string BytesToBase64(const std::vector<uint8_t>& bytes) {
return base::Base64Encode(bytes);
}
#endif
} // namespace
// The anonymous namespace needs to end here because of `friend`ships between
// the tests and the production code.
using CreditCardAccessManagerTest = CreditCardAccessManagerTestBase;
// Params of the
// CreditCardAccessManagerAuthFlowTest:
// -- bool masked_server_card_risk_based_auth_enabled;
class CreditCardAccessManagerAuthFlowTest
: public testing::WithParamInterface<bool>,
public CreditCardAccessManagerTestBase {
public:
CreditCardAccessManagerAuthFlowTest() = default;
~CreditCardAccessManagerAuthFlowTest() override = default;
bool IsMaskedServerCardRiskBasedAuthEnabled() { return GetParam(); }
void FetchCreditCardAndCompleteRiskBasedAuthIfAvailable(
const CreditCard* card,
base::Value::Dict fido_request_options = base::Value::Dict()) {
CreditCardAccessManagerTestBase::FetchCreditCard(card);
if (!IsMaskedServerCardRiskBasedAuthEnabled()) {
return;
}
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_ANDROID)
credit_card_access_manager().OnRiskBasedAuthenticationResponseReceived(
CreditCardRiskBasedAuthenticator::RiskBasedAuthenticationResponse()
.with_result(CreditCardRiskBasedAuthenticator::
RiskBasedAuthenticationResponse::Result::
kAuthenticationRequired)
.with_fido_request_options(std::move(fido_request_options))
.with_context_token("fake context token"));
#endif
}
protected:
void SetUp() override {
CreditCardAccessManagerTestBase::SetUp();
if (IsMaskedServerCardRiskBasedAuthEnabled()) {
#if !BUILDFLAG(IS_WIN) && !BUILDFLAG(IS_MAC) && !BUILDFLAG(IS_ANDROID)
GTEST_SKIP() << "Skipping test because masked server card risk-based "
"flow should only happen on WIN, MAC or ANDROID";
#endif
}
feature_list_.InitWithFeatureState(
features::kAutofillEnableFpanRiskBasedAuthentication,
IsMaskedServerCardRiskBasedAuthEnabled());
}
private:
base::test::ScopedFeatureList feature_list_;
};
INSTANTIATE_TEST_SUITE_P(,
CreditCardAccessManagerAuthFlowTest,
testing::Bool());
// Tests retrieving local cards.
TEST_F(CreditCardAccessManagerTest, FetchLocalCardSuccess) {
#if BUILDFLAG(IS_ANDROID)
if (base::android::device_info::is_automotive()) {
GTEST_SKIP() << "This test should not run on automotive.";
}
#endif // BUILDFLAG(IS_ANDROID)
CreateLocalCard(kTestGUID, kTestNumber);
const CreditCard* card =
personal_data().payments_data_manager().GetCreditCardByGUID(kTestGUID);
PrepareToFetchCreditCardAndWaitForCallbacks();
FetchCreditCard(card);
EXPECT_EQ(kTestNumber16, accessor_->number());
EXPECT_EQ(kTestCvc16, accessor_->cvc());
// There was no interactive authentication in this flow, so check that this
// is signaled correctly.
std::optional<NonInteractivePaymentMethodType> type =
test_api(*autofill_client_.GetFormDataImporter())
.payment_method_type_if_non_interactive_authentication_flow_completed();
ASSERT_TRUE(type.has_value());
ASSERT_EQ(type.value(), NonInteractivePaymentMethodType::kLocalCard);
}
// Tests that fetching a local card correctly updates the fetched payments data
// context in FormDataImporter. Exists to ensure crbug.com/448461590 stays
// fixed.
TEST_F(CreditCardAccessManagerTest, FetchLocalCard_UpdatesPaymentsContext) {
int64_t instrument_id = 12345;
CreditCard local_card = test::GetCreditCard();
local_card.set_instrument_id(instrument_id);
personal_data().payments_data_manager().AddCreditCard(local_card);
const CreditCard* card =
personal_data().payments_data_manager().GetCreditCardByGUID(
local_card.guid());
auto* form_data_importer = autofill_client_.GetFormDataImporter();
ASSERT_TRUE(form_data_importer);
PrepareToFetchCreditCardAndWaitForCallbacks();
FetchCreditCard(card);
const auto& context = form_data_importer->fetched_payments_data_context();
EXPECT_EQ(context.fetched_card_instrument_id, instrument_id);
ASSERT_TRUE(context.card_was_fetched_from_cache.has_value());
EXPECT_FALSE(*context.card_was_fetched_from_cache);
}
// Ensures that FetchCreditCard() returns the full PAN upon a successful
// response from payments.
TEST_P(CreditCardAccessManagerAuthFlowTest, FetchServerCardCVCSuccess) {
CreateServerCard(kTestGUID, kTestNumber);
const CreditCard* card =
personal_data().payments_data_manager().GetCreditCardByGUID(kTestGUID);
base::HistogramTester histogram_tester;
std::string flow_events_histogram_name = "Autofill.BetterAuth.FlowEvents.Cvc";
PrepareToFetchCreditCardAndWaitForCallbacks();
FetchCreditCardAndCompleteRiskBasedAuthIfAvailable(card);
histogram_tester.ExpectUniqueSample(
flow_events_histogram_name,
CreditCardFormEventLogger::UnmaskAuthFlowEvent::kPromptShown, 1);
EXPECT_TRUE(GetRealPanForCVCAuth(PaymentsRpcResult::kSuccess, kTestNumber));
EXPECT_EQ(kTestNumber16, accessor_->number());
EXPECT_EQ(kTestCvc16, accessor_->cvc());
histogram_tester.ExpectBucketCount(
flow_events_histogram_name,
CreditCardFormEventLogger::UnmaskAuthFlowEvent::kPromptCompleted, 1);
histogram_tester.ExpectUniqueSample(
"Autofill.ServerCardUnmask.ServerCard.Attempt", true, 1);
// Expect that we did not signal that there was no interactive
// authentication.
EXPECT_FALSE(
test_api(*autofill_client_.GetFormDataImporter())
.payment_method_type_if_non_interactive_authentication_flow_completed()
.has_value());
}
// Ensures that FetchCreditCard() returns a failure upon a negative response
// from the server.
TEST_P(CreditCardAccessManagerAuthFlowTest, FetchServerCardCVCNetworkError) {
CreateServerCard(kTestGUID, kTestNumber);
const CreditCard* card =
personal_data().payments_data_manager().GetCreditCardByGUID(kTestGUID);
PrepareToFetchCreditCardAndWaitForCallbacks();
FetchCreditCardAndCompleteRiskBasedAuthIfAvailable(card);
EXPECT_TRUE(
GetRealPanForCVCAuth(PaymentsRpcResult::kNetworkError, std::string()));
}
// Ensures that FetchCreditCard() returns a failure upon a negative response
// from the server.
TEST_P(CreditCardAccessManagerAuthFlowTest,
FetchServerCardCVCPermanentFailure) {
CreateServerCard(kTestGUID, kTestNumber);
const CreditCard* card =
personal_data().payments_data_manager().GetCreditCardByGUID(kTestGUID);
PrepareToFetchCreditCardAndWaitForCallbacks();
FetchCreditCardAndCompleteRiskBasedAuthIfAvailable(card);
EXPECT_TRUE(GetRealPanForCVCAuth(PaymentsRpcResult::kPermanentFailure,
std::string()));
}
// Ensures that a "try again" response from payments does not end the flow.
TEST_P(CreditCardAccessManagerAuthFlowTest, FetchServerCardCVCTryAgainFailure) {
CreateServerCard(kTestGUID, kTestNumber);
const CreditCard* card =
personal_data().payments_data_manager().GetCreditCardByGUID(kTestGUID);
FetchCreditCardAndCompleteRiskBasedAuthIfAvailable(card);
EXPECT_TRUE(
GetRealPanForCVCAuth(PaymentsRpcResult::kTryAgainFailure, std::string()));
EXPECT_TRUE(GetRealPanForCVCAuth(PaymentsRpcResult::kSuccess, kTestNumber));
EXPECT_EQ(kTestNumber16, accessor_->number());
EXPECT_EQ(kTestCvc16, accessor_->cvc());
}
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_ANDROID)
// Ensures that FetchCreditCard() returns the full PAN upon a successful
// WebAuthn verification and response from payments.
TEST_P(CreditCardAccessManagerAuthFlowTest, FetchServerCardFIDOSuccess) {
base::HistogramTester histogram_tester;
std::string unmask_decision_histogram_name =
"Autofill.BetterAuth.CardUnmaskTypeDecision";
std::string webauthn_result_histogram_name =
"Autofill.BetterAuth.WebauthnResult.ImmediateAuthentication";
std::string flow_events_histogram_name =
"Autofill.BetterAuth.FlowEvents.Fido";
CreateServerCard(kTestGUID, kTestNumber);
const CreditCard* card =
personal_data().payments_data_manager().GetCreditCardByGUID(kTestGUID);
GetFIDOAuthenticator()->SetUserVerifiable(true);
SetCreditCardFIDOAuthEnabled(true);
payments_network_interface().AddFidoEligibleCard(
card->server_id(), kCredentialId, kGooglePaymentsRpid);
PrepareToFetchCreditCardAndWaitForCallbacks();
FetchCreditCardAndCompleteRiskBasedAuthIfAvailable(
card, /*fido_request_options=*/GetTestRequestOptions());
WaitForCallbacks();
histogram_tester.ExpectUniqueSample(
flow_events_histogram_name,
CreditCardFormEventLogger::UnmaskAuthFlowEvent::kPromptShown, 1);
// FIDO Success.
EXPECT_EQ(CreditCardFidoAuthenticator::Flow::AUTHENTICATION_FLOW,
GetFIDOAuthenticator()->current_flow());
TestCreditCardFidoAuthenticator::GetAssertion(GetFIDOAuthenticator(),
/*did_succeed=*/true);
EXPECT_TRUE(GetRealPanForFIDOAuth(PaymentsRpcResult::kSuccess, kTestNumber));
EXPECT_EQ(kCredentialId,
BytesToBase64(GetFIDOAuthenticator()->GetCredentialId()));
EXPECT_EQ(kTestNumber16, accessor_->number());
EXPECT_EQ(kTestCvc16, accessor_->cvc());
histogram_tester.ExpectUniqueSample(
unmask_decision_histogram_name,
autofill_metrics::CardUnmaskTypeDecisionMetric::kFidoOnly, 1);
histogram_tester.ExpectUniqueSample(
webauthn_result_histogram_name,
autofill_metrics::WebauthnResultMetric::kSuccess, 1);
histogram_tester.ExpectTotalCount(
"Autofill.BetterAuth.CardUnmaskDuration.Fido", 1);
histogram_tester.ExpectTotalCount(
"Autofill.BetterAuth.CardUnmaskDuration.Fido.ServerCard.Success", 1);
histogram_tester.ExpectBucketCount(
flow_events_histogram_name,
CreditCardFormEventLogger::UnmaskAuthFlowEvent::kPromptCompleted, 1);
}
// Ensures that CVC filling gets logged after FIDO success if the card has CVC.
TEST_P(CreditCardAccessManagerAuthFlowTest, LogCvcFillingFIDOSuccess) {
base::HistogramTester histogram_tester;
CreditCard server_card = test::WithCvc(test::GetMaskedServerCard());
personal_data().test_payments_data_manager().AddServerCreditCard(server_card);
const CreditCard* card =
personal_data().payments_data_manager().GetCreditCardByInstrumentId(
server_card.instrument_id());
GetFIDOAuthenticator()->SetUserVerifiable(true);
SetCreditCardFIDOAuthEnabled(true);
payments_network_interface().AddFidoEligibleCard(
card->server_id(), kCredentialId, kGooglePaymentsRpid);
PrepareToFetchCreditCardAndWaitForCallbacks();
FetchCreditCardAndCompleteRiskBasedAuthIfAvailable(
card, /*fido_request_options=*/GetTestRequestOptions());
WaitForCallbacks();
// FIDO Success.
TestCreditCardFidoAuthenticator::GetAssertion(GetFIDOAuthenticator(),
/*did_succeed=*/true);
EXPECT_TRUE(GetRealPanForFIDOAuth(PaymentsRpcResult::kSuccess, kTestNumber));
histogram_tester.ExpectUniqueSample(
"Autofill.CvcStorage.CvcFilling.ServerCard",
autofill_metrics::CvcFillingFlowType::kFido, 1);
}
// Ensures that CVC filling doesn't get logged after FIDO success if the card
// doesn't have CVC.
TEST_P(CreditCardAccessManagerAuthFlowTest, DoNotLogCvcFillingFIDOSuccess) {
base::HistogramTester histogram_tester;
CreditCard server_card = test::GetMaskedServerCard();
server_card.set_cvc(u"");
personal_data().test_payments_data_manager().AddServerCreditCard(server_card);
const CreditCard* card =
personal_data().payments_data_manager().GetCreditCardByInstrumentId(
server_card.instrument_id());
GetFIDOAuthenticator()->SetUserVerifiable(true);
SetCreditCardFIDOAuthEnabled(true);
payments_network_interface().AddFidoEligibleCard(
card->server_id(), kCredentialId, kGooglePaymentsRpid);
PrepareToFetchCreditCardAndWaitForCallbacks();
FetchCreditCardAndCompleteRiskBasedAuthIfAvailable(
card, /*fido_request_options=*/GetTestRequestOptions());
WaitForCallbacks();
// FIDO Success.
TestCreditCardFidoAuthenticator::GetAssertion(GetFIDOAuthenticator(),
/*did_succeed=*/true);
EXPECT_TRUE(GetRealPanForFIDOAuth(PaymentsRpcResult::kSuccess, kTestNumber));
histogram_tester.ExpectUniqueSample(
"Autofill.CvcStorage.CvcFilling.ServerCard",
autofill_metrics::CvcFillingFlowType::kFido, 0);
}
// Ensures that CVC filling gets logged if a card with CVC is retrieved with
// non-interactive authentication.
TEST_F(CreditCardAccessManagerTest,
LogCvcFillingWithoutInteractiveAuthentication) {
base::HistogramTester histogram_tester;
CreditCard local_card = test::WithCvc(test::GetCreditCard());
personal_data().payments_data_manager().AddCreditCard(local_card);
const CreditCard* card =
personal_data().payments_data_manager().GetCreditCardByGUID(
local_card.guid());
PrepareToFetchCreditCardAndWaitForCallbacks();
FetchCreditCard(card);
histogram_tester.ExpectUniqueSample(
"Autofill.CvcStorage.CvcFilling.LocalCard",
autofill_metrics::CvcFillingFlowType::kNoInteractiveAuthentication, 1);
}
// Ensures that CVC filling doesn't get logged if a card without CVC is
// retrieved with non-interactive authentication
TEST_F(CreditCardAccessManagerTest,
DoNotLogCvcFillingWithoutInteractiveAuthentication) {
base::HistogramTester histogram_tester;
CreditCard local_card = test::GetCreditCard();
personal_data().payments_data_manager().AddCreditCard(local_card);
const CreditCard* card =
personal_data().payments_data_manager().GetCreditCardByGUID(
local_card.guid());
PrepareToFetchCreditCardAndWaitForCallbacks();
FetchCreditCard(card);
histogram_tester.ExpectUniqueSample(
"Autofill.CvcStorage.CvcFilling.LocalCard",
autofill_metrics::CvcFillingFlowType::kNoInteractiveAuthentication, 0);
}
// Ensures that accessor retrieve empty CVC upon a successful
// WebAuthn verification and response from payments using masked server card
// without CVC.
TEST_P(CreditCardAccessManagerAuthFlowTest,
FetchServerCardWithoutCvcFIDOSuccess) {
CreditCard server_card = CreditCard();
test::SetCreditCardInfo(&server_card, "Elvis Presley", kTestNumber,
test::NextMonth().c_str(), test::NextYear().c_str(),
"1", u"");
server_card.set_guid(kTestGUID);
server_card.set_record_type(CreditCard::RecordType::kMaskedServerCard);
personal_data().test_payments_data_manager().AddServerCreditCard(server_card);
const CreditCard* card =
personal_data().payments_data_manager().GetCreditCardByGUID(kTestGUID);
GetFIDOAuthenticator()->SetUserVerifiable(true);
SetCreditCardFIDOAuthEnabled(true);
payments_network_interface().AddFidoEligibleCard(
card->server_id(), kCredentialId, kGooglePaymentsRpid);
PrepareToFetchCreditCardAndWaitForCallbacks();
FetchCreditCardAndCompleteRiskBasedAuthIfAvailable(
card, /*fido_request_options=*/GetTestRequestOptions());
WaitForCallbacks();
// FIDO Success.
EXPECT_EQ(CreditCardFidoAuthenticator::Flow::AUTHENTICATION_FLOW,
GetFIDOAuthenticator()->current_flow());
TestCreditCardFidoAuthenticator::GetAssertion(GetFIDOAuthenticator(),
/*did_succeed=*/true);
EXPECT_TRUE(GetRealPanForFIDOAuth(PaymentsRpcResult::kSuccess, kTestNumber));
EXPECT_EQ(u"", accessor_->cvc());
}
// Ensures that FetchCreditCard() returns the full PAN upon a successful
// WebAuthn verification and response from payments.
TEST_P(CreditCardAccessManagerAuthFlowTest,
FetchServerCardFIDOSuccessWithDcvv) {
// Opt user in for FIDO auth.
prefs::SetCreditCardFIDOAuthEnabled(autofill_client_.GetPrefs(), true);
// General setup.
CreateServerCard(kTestGUID, kTestNumber);
const CreditCard* card =
personal_data().payments_data_manager().GetCreditCardByGUID(kTestGUID);
GetFIDOAuthenticator()->SetUserVerifiable(true);
payments_network_interface().AddFidoEligibleCard(
card->server_id(), kCredentialId, kGooglePaymentsRpid);
PrepareToFetchCreditCardAndWaitForCallbacks();
FetchCreditCardAndCompleteRiskBasedAuthIfAvailable(
card, /*fido_request_options=*/GetTestRequestOptions());
WaitForCallbacks();
// FIDO Success.
TestCreditCardFidoAuthenticator::GetAssertion(GetFIDOAuthenticator(),
/*did_succeed=*/true);
// Mock Payments response that includes DCVV along with Full PAN.
EXPECT_TRUE(GetRealPanForFIDOAuth(PaymentsRpcResult::kSuccess, kTestNumber,
kTestCvc));
// Expect accessor to successfully retrieve the DCVV.
EXPECT_EQ(kTestNumber16, accessor_->number());
EXPECT_EQ(kTestCvc16, accessor_->cvc());
}
// Ensures that CVC prompt is invoked after WebAuthn fails.
TEST_P(CreditCardAccessManagerAuthFlowTest,
FetchServerCardFIDOVerificationFailureCVCFallback) {
base::HistogramTester histogram_tester;
std::string webauthn_result_histogram_name =
"Autofill.BetterAuth.WebauthnResult.ImmediateAuthentication";
std::string flow_events_fido_histogram_name =
"Autofill.BetterAuth.FlowEvents.Fido";
std::string flow_events_cvc_fallback_histogram_name =
"Autofill.BetterAuth.FlowEvents.CvcFallbackFromFido";
CreateServerCard(kTestGUID, kTestNumber);
const CreditCard* card =
personal_data().payments_data_manager().GetCreditCardByGUID(kTestGUID);
GetFIDOAuthenticator()->SetUserVerifiable(true);
SetCreditCardFIDOAuthEnabled(true);
payments_network_interface().AddFidoEligibleCard(
card->server_id(), kCredentialId, kGooglePaymentsRpid);
PrepareToFetchCreditCardAndWaitForCallbacks();
FetchCreditCardAndCompleteRiskBasedAuthIfAvailable(
card, /*fido_request_options=*/GetTestRequestOptions());
WaitForCallbacks();
histogram_tester.ExpectUniqueSample(
flow_events_fido_histogram_name,
CreditCardFormEventLogger::UnmaskAuthFlowEvent::kPromptShown, 1);
// FIDO Failure.
EXPECT_EQ(CreditCardFidoAuthenticator::Flow::AUTHENTICATION_FLOW,
GetFIDOAuthenticator()->current_flow());
TestCreditCardFidoAuthenticator::GetAssertion(GetFIDOAuthenticator(),
/*did_succeed=*/false);
histogram_tester.ExpectBucketCount(
flow_events_cvc_fallback_histogram_name,
CreditCardFormEventLogger::UnmaskAuthFlowEvent::kPromptShown, 1);
EXPECT_FALSE(GetRealPanForFIDOAuth(PaymentsRpcResult::kSuccess, kTestNumber));
// Followed by a fallback to CVC.
EXPECT_EQ(CreditCardFidoAuthenticator::Flow::NONE_FLOW,
GetFIDOAuthenticator()->current_flow());
EXPECT_TRUE(GetRealPanForCVCAuth(PaymentsRpcResult::kSuccess, kTestNumber));
EXPECT_EQ(kTestNumber16, accessor_->number());
EXPECT_EQ(kTestCvc16, accessor_->cvc());
histogram_tester.ExpectUniqueSample(
webauthn_result_histogram_name,
autofill_metrics::WebauthnResultMetric::kNotAllowedError, 1);
histogram_tester.ExpectBucketCount(
flow_events_fido_histogram_name,
CreditCardFormEventLogger::UnmaskAuthFlowEvent::kPromptCompleted, 0);
histogram_tester.ExpectBucketCount(
flow_events_cvc_fallback_histogram_name,
CreditCardFormEventLogger::UnmaskAuthFlowEvent::kPromptCompleted, 1);
}
// Ensures that CVC prompt is invoked after payments returns an error from
// GetRealPan via FIDO.
TEST_P(CreditCardAccessManagerAuthFlowTest,
FetchServerCardFIDOServerFailureCVCFallback) {
base::HistogramTester histogram_tester;
std::string histogram_name =
"Autofill.BetterAuth.WebauthnResult.ImmediateAuthentication";
CreateServerCard(kTestGUID, kTestNumber);
const CreditCard* card =
personal_data().payments_data_manager().GetCreditCardByGUID(kTestGUID);
GetFIDOAuthenticator()->SetUserVerifiable(true);
SetCreditCardFIDOAuthEnabled(true);
payments_network_interface().AddFidoEligibleCard(
card->server_id(), kCredentialId, kGooglePaymentsRpid);
PrepareToFetchCreditCardAndWaitForCallbacks();
FetchCreditCardAndCompleteRiskBasedAuthIfAvailable(
card, /*fido_request_options=*/GetTestRequestOptions());
WaitForCallbacks();
// FIDO Failure.
EXPECT_EQ(CreditCardFidoAuthenticator::Flow::AUTHENTICATION_FLOW,
GetFIDOAuthenticator()->current_flow());
TestCreditCardFidoAuthenticator::GetAssertion(GetFIDOAuthenticator(),
/*did_succeed=*/true);
EXPECT_TRUE(
GetRealPanForFIDOAuth(PaymentsRpcResult::kPermanentFailure, kTestNumber));
// Followed by a fallback to CVC.
EXPECT_EQ(CreditCardFidoAuthenticator::Flow::NONE_FLOW,
GetFIDOAuthenticator()->current_flow());
EXPECT_TRUE(GetRealPanForCVCAuth(PaymentsRpcResult::kSuccess, kTestNumber));
EXPECT_EQ(kTestNumber16, accessor_->number());
EXPECT_EQ(kTestCvc16, accessor_->cvc());
histogram_tester.ExpectUniqueSample(
histogram_name, autofill_metrics::WebauthnResultMetric::kSuccess, 1);
histogram_tester.ExpectTotalCount(
"Autofill.BetterAuth.CardUnmaskDuration.Fido", 1);
histogram_tester.ExpectTotalCount(
"Autofill.BetterAuth.CardUnmaskDuration.Fido.ServerCard.Failure", 1);
}
// Ensures WebAuthn call is not made if Request Options is missing a Credential
// ID, and falls back to CVC.
TEST_P(CreditCardAccessManagerAuthFlowTest,
FetchServerCardBadRequestOptionsCVCFallback) {
CreateServerCard(kTestGUID, kTestNumber);
const CreditCard* card =
personal_data().payments_data_manager().GetCreditCardByGUID(kTestGUID);
GetFIDOAuthenticator()->SetUserVerifiable(true);
SetCreditCardFIDOAuthEnabled(true);
// Don't set Credential ID.
payments_network_interface().AddFidoEligibleCard(
card->server_id(), /*credential_id=*/"", kGooglePaymentsRpid);
PrepareToFetchCreditCardAndWaitForCallbacks();
FetchCreditCardAndCompleteRiskBasedAuthIfAvailable(card);
WaitForCallbacks();
// FIDO Failure.
EXPECT_FALSE(GetRealPanForFIDOAuth(PaymentsRpcResult::kSuccess, kTestNumber));
// Followed by a fallback to CVC.
EXPECT_TRUE(GetRealPanForCVCAuth(PaymentsRpcResult::kSuccess, kTestNumber));
EXPECT_EQ(kTestNumber16, accessor_->number());
EXPECT_EQ(kTestCvc16, accessor_->cvc());
}
// Ensures that CVC prompt is invoked when the pre-flight call to Google
// Payments times out.
TEST_P(CreditCardAccessManagerAuthFlowTest,
FetchServerCardFIDOTimeoutCVCFallback) {
if (IsMaskedServerCardRiskBasedAuthEnabled()) {
GTEST_SKIP() << "For risk based authentication, whether fallback to FIDO "
"or CVC flow doesn't rely on the platform call";
}
CreateServerCard(kTestGUID, kTestNumber);
const CreditCard* card =
personal_data().payments_data_manager().GetCreditCardByGUID(kTestGUID);
GetFIDOAuthenticator()->SetUserVerifiable(true);
SetCreditCardFIDOAuthEnabled(true);
FetchCreditCard(card);
InvokeUnmaskDetailsTimeout();
WaitForCallbacks();
EXPECT_TRUE(GetRealPanForCVCAuth(PaymentsRpcResult::kSuccess, kTestNumber));
EXPECT_EQ(kTestNumber16, accessor_->number());
EXPECT_EQ(kTestCvc16, accessor_->cvc());
}
// Ensures whether user choose a masked server card before or after the
// preflight call is returned is correctly logged.
TEST_P(CreditCardAccessManagerAuthFlowTest,
Metrics_LoggingPreflightCallReturnedBeforeUserSelection) {
// Setting up a FIDO-enabled user with a local card and a server card.
std::string server_guid = "00000000-0000-0000-0000-000000000001";
std::string local_guid = "00000000-0000-0000-0000-000000000003";
CreateServerCard(server_guid, "4594299181086168");
CreateLocalCard(local_guid, "4409763681177079");
const CreditCard* server_card =
personal_data().payments_data_manager().GetCreditCardByGUID(server_guid);
const CreditCard* local_card =
personal_data().payments_data_manager().GetCreditCardByGUID(local_guid);
GetFIDOAuthenticator()->SetUserVerifiable(true);
for (bool user_is_opted_in : {true, false}) {
std::string histogram_name;
if (IsMaskedServerCardRiskBasedAuthEnabled()) {
histogram_name =
"Autofill.BetterAuth.PreflightCallResponseReceivedOnCardSelection.";
histogram_name +=
user_is_opted_in ? "OptedIn.ServerCard" : "OptedOut.ServerCard";
} else {
histogram_name =
"Autofill.BetterAuth.UserPerceivedLatencyOnCardSelection.";
histogram_name += user_is_opted_in ? "OptedIn" : "OptedOut";
}
SetCreditCardFIDOAuthEnabled(user_is_opted_in);
{
// Preflight call ignored because local card was chosen.
base::HistogramTester histogram_tester;
ResetFetchCreditCard();
credit_card_access_manager().PrepareToFetchCreditCard();
task_environment_.FastForwardBy(base::Seconds(4));
WaitForCallbacks();
FetchCreditCard(local_card);
WaitForCallbacks();
histogram_tester.ExpectUniqueSample(
std::string(
"Autofill.BetterAuth.UserPerceivedLatencyOnCardSelection.") +
(user_is_opted_in ? "OptedIn" : "OptedOut"),
autofill_metrics::PreflightCallEvent::kDidNotChooseMaskedCard, 1);
}
{
// Preflight call returned after card was chosen.
base::HistogramTester histogram_tester;
payments_network_interface().ShouldReturnUnmaskDetailsImmediately(false);
ResetFetchCreditCard();
credit_card_access_manager().PrepareToFetchCreditCard();
FetchCreditCard(server_card);
task_environment_.FastForwardBy(base::Seconds(4));
WaitForCallbacks();
histogram_tester.ExpectUniqueSample(
histogram_name,
autofill_metrics::PreflightCallEvent::
kCardChosenBeforePreflightCallReturned,
1);
if (!IsMaskedServerCardRiskBasedAuthEnabled()) {
histogram_tester.ExpectTotalCount(
"Autofill.BetterAuth.UserPerceivedLatencyOnCardSelection.OptedIn."
"Duration",
static_cast<int>(user_is_opted_in));
histogram_tester.ExpectTotalCount(
"Autofill.BetterAuth.UserPerceivedLatencyOnCardSelection.OptedIn."
"TimedOutCvcFallback",
static_cast<int>(user_is_opted_in));
}
}
{
// Preflight call returned before card was chosen.
base::HistogramTester histogram_tester;
// This is important because CreditCardFidoAuthenticator will update the
// opted-in pref according to GetDetailsForGetRealPan response.
payments_network_interface().AllowFidoRegistration(!user_is_opted_in);
ResetFetchCreditCard();
PrepareToFetchCreditCardAndWaitForCallbacks();
FetchCreditCard(server_card);
WaitForCallbacks();
histogram_tester.ExpectUniqueSample(
histogram_name,
autofill_metrics::PreflightCallEvent::
kPreflightCallReturnedBeforeCardChosen,
1);
}
}
}
// Ensures that falling back to CVC because of preflight timeout is correctly
// logged.
TEST_P(CreditCardAccessManagerAuthFlowTest,
Metrics_LoggingTimedOutCvcFallback) {
if (IsMaskedServerCardRiskBasedAuthEnabled()) {
GTEST_SKIP() << "For risk based authentication, whether fallback to FIDO "
"or CVC flow doesn't rely on the platform call";
}
// Setting up a FIDO-enabled user with a local card and a server card.
std::string server_guid = "00000000-0000-0000-0000-000000000001";
CreateServerCard(server_guid, "4594299181086168");
const CreditCard* server_card =
personal_data().payments_data_manager().GetCreditCardByGUID(server_guid);
GetFIDOAuthenticator()->SetUserVerifiable(true);
SetCreditCardFIDOAuthEnabled(true);
payments_network_interface().ShouldReturnUnmaskDetailsImmediately(false);
std::string existence_perceived_latency_histogram_name =
"Autofill.BetterAuth.UserPerceivedLatencyOnCardSelection.OptedIn";
std::string perceived_latency_duration_histogram_name =
"Autofill.BetterAuth.UserPerceivedLatencyOnCardSelection.OptedIn."
"Duration";
std::string timeout_cvc_fallback_histogram_name =
"Autofill.BetterAuth.UserPerceivedLatencyOnCardSelection.OptedIn."
"TimedOutCvcFallback";
// Preflight call arrived before timeout, after card was chosen.
{
base::HistogramTester histogram_tester;
ResetFetchCreditCard();
credit_card_access_manager().PrepareToFetchCreditCard();
FetchCreditCard(server_card);
// Mock a delayed response.
InvokeDelayedGetUnmaskDetailsResponse();
task_environment_.FastForwardBy(base::Seconds(4));
WaitForCallbacks();
histogram_tester.ExpectUniqueSample(
existence_perceived_latency_histogram_name,
autofill_metrics::PreflightCallEvent::
kCardChosenBeforePreflightCallReturned,
1);
histogram_tester.ExpectTotalCount(perceived_latency_duration_histogram_name,
1);
histogram_tester.ExpectBucketCount(timeout_cvc_fallback_histogram_name,
false, 1);
}
// Preflight call timed out and CVC fallback was invoked.
{
base::HistogramTester histogram_tester;
ResetFetchCreditCard();
credit_card_access_manager().PrepareToFetchCreditCard();
FetchCreditCard(server_card);
task_environment_.FastForwardBy(base::Seconds(4));
WaitForCallbacks();
histogram_tester.ExpectUniqueSample(
existence_perceived_latency_histogram_name,
autofill_metrics::PreflightCallEvent::
kCardChosenBeforePreflightCallReturned,
1);
histogram_tester.ExpectTotalCount(perceived_latency_duration_histogram_name,
1);
histogram_tester.ExpectBucketCount(timeout_cvc_fallback_histogram_name,
true, 1);
}
}
// Ensures that use of a server card that is not enrolled into FIDO invokes
// authorization flow when user is opted-in.
TEST_P(CreditCardAccessManagerAuthFlowTest, FIDONewCardAuthorization) {
base::HistogramTester histogram_tester;
std::string unmask_decision_histogram_name =
"Autofill.BetterAuth.CardUnmaskTypeDecision";
std::string webauthn_result_histogram_name =
"Autofill.BetterAuth.WebauthnResult.AuthenticationAfterCVC";
std::string flow_events_histogram_name =
"Autofill.BetterAuth.FlowEvents.CvcThenFido";
CreateServerCard(kTestGUID, kTestNumber);
const CreditCard* card =
personal_data().payments_data_manager().GetCreditCardByGUID(kTestGUID);
OptUserInToFido();
payments_network_interface().ShouldReturnUnmaskDetailsImmediately(true);
payments_network_interface().SetFidoRequestOptionsInUnmaskDetails(
kCredentialId, kGooglePaymentsRpid);
PrepareToFetchCreditCardAndWaitForCallbacks();
FetchCreditCardAndCompleteRiskBasedAuthIfAvailable(card);
InvokeUnmaskDetailsTimeout();
WaitForCallbacks();
histogram_tester.ExpectUniqueSample(
flow_events_histogram_name,
CreditCardFormEventLogger::UnmaskAuthFlowEvent::kPromptShown, 1);
// Do not return any RequestOptions or CreationOptions in GetRealPan.
// RequestOptions should have been returned in unmask details response.
EXPECT_TRUE(GetRealPanForCVCAuth(PaymentsRpcResult::kSuccess, kTestNumber,
TestFidoRequestOptionsType::kNotPresent));
// Ensure that the form is not filled yet (OnCreditCardFetched is not called).
EXPECT_EQ(accessor_->number(), std::u16string());
EXPECT_EQ(accessor_->cvc(), std::u16string());
// Mock user response.
EXPECT_EQ(CreditCardFidoAuthenticator::Flow::FOLLOWUP_AFTER_CVC_AUTH_FLOW,
GetFIDOAuthenticator()->current_flow());
TestCreditCardFidoAuthenticator::GetAssertion(GetFIDOAuthenticator(),
/*did_succeed=*/true);
// Ensure that the form is filled after user verification (OnCreditCardFetched
// is called).
EXPECT_EQ(kTestNumber16, accessor_->number());
EXPECT_EQ(kTestCvc16, accessor_->cvc());
// Mock OptChange payments call.
OptChange(PaymentsRpcResult::kSuccess, true);
histogram_tester.ExpectUniqueSample(
unmask_decision_histogram_name,
autofill_metrics::CardUnmaskTypeDecisionMetric::kCvcThenFido, 1);
histogram_tester.ExpectUniqueSample(
webauthn_result_histogram_name,
autofill_metrics::WebauthnResultMetric::kSuccess, 1);
histogram_tester.ExpectBucketCount(
flow_events_histogram_name,
CreditCardFormEventLogger::UnmaskAuthFlowEvent::kPromptCompleted, 1);
}
// Ensures that the use of a server card that is not enrolled into FIDO fills
// the form if the user is opted-in to FIDO but no request options are present.
TEST_P(CreditCardAccessManagerAuthFlowTest,
FIDONewCardAuthorization_NoRequestOptions_FormFilled) {
const CreditCard* card = CreateServerCard(kTestGUID, kTestNumber);
OptUserInToFido();
// Clear the FIDO request options that were set.
payments_network_interface().unmask_details()->fido_request_options.clear();
payments_network_interface().ShouldReturnUnmaskDetailsImmediately(true);
PrepareToFetchCreditCardAndWaitForCallbacks();
FetchCreditCardAndCompleteRiskBasedAuthIfAvailable(card);
InvokeUnmaskDetailsTimeout();
WaitForCallbacks();
// Do not return any RequestOptions or CreationOptions in GetRealPan.
EXPECT_TRUE(GetRealPanForCVCAuth(PaymentsRpcResult::kSuccess, kTestNumber,
TestFidoRequestOptionsType::kNotPresent));
// Ensure that the form is filled as there are no FIDO request options
// present.
EXPECT_EQ(accessor_->number(), kTestNumber16);
EXPECT_EQ(accessor_->cvc(), kTestCvc16);
}
// Ensures that use of a server card that is not enrolled into FIDO fills the
// form if the user is opted-in to FIDO but the request options are invalid.
TEST_P(CreditCardAccessManagerAuthFlowTest,
FIDONewCardAuthorization_InvalidRequestOptions_FormFilled) {
const CreditCard* card = CreateServerCard(kTestGUID, kTestNumber);
OptUserInToFido();
payments_network_interface().ShouldReturnUnmaskDetailsImmediately(true);
// Set invalid FIDO request options.
payments_network_interface().SetFidoRequestOptionsInUnmaskDetails(
/*credential_id=*/"", /*relying_party_id=*/"");
PrepareToFetchCreditCardAndWaitForCallbacks();
FetchCreditCardAndCompleteRiskBasedAuthIfAvailable(
card, /*fido_request_options=*/GetTestRequestOptions(
/*return_invalid_request_options=*/true));
InvokeUnmaskDetailsTimeout();
WaitForCallbacks();
// Do not return any RequestOptions in GetRealPan. RequestOptions should have
// been returned in unmask details response.
EXPECT_TRUE(GetRealPanForCVCAuth(PaymentsRpcResult::kSuccess, kTestNumber,
TestFidoRequestOptionsType::kNotPresent));
// Ensure that the form is filled as the only FIDO request options present are
// invalid.
EXPECT_EQ(accessor_->number(), kTestNumber16);
EXPECT_EQ(accessor_->cvc(), kTestCvc16);
}
// Ensures expired cards always invoke a CVC prompt instead of WebAuthn.
TEST_F(CreditCardAccessManagerTest, FetchExpiredServerCardInvokesCvcPrompt) {
// Creating an expired server card and opting the user in with authorized
// card.
CreateServerCard(kTestGUID, kTestNumber);
CreditCard card =
*personal_data().payments_data_manager().GetCreditCardByGUID(kTestGUID);
card.SetExpirationYearFromString(u"2010");
personal_data().payments_data_manager().UpdateCreditCard(card);
GetFIDOAuthenticator()->SetUserVerifiable(true);
SetCreditCardFIDOAuthEnabled(true);
payments_network_interface().AddFidoEligibleCard(
card.server_id(), kCredentialId, kGooglePaymentsRpid);
PrepareToFetchCreditCardAndWaitForCallbacks();
FetchCreditCard(&card);
WaitForCallbacks();
// Expect CVC prompt to be invoked.
EXPECT_TRUE(GetRealPanForCVCAuth(PaymentsRpcResult::kSuccess, kTestNumber));
EXPECT_EQ(kTestNumber16, accessor_->number());
EXPECT_EQ(kTestCvc16, accessor_->cvc());
}
#if BUILDFLAG(IS_ANDROID)
// Ensures that the WebAuthn verification prompt is invoked after user opts in
// on unmask card checkbox.
TEST_P(CreditCardAccessManagerAuthFlowTest, FIDOOptInSuccess_Android) {
base::HistogramTester histogram_tester;
std::string histogram_name =
"Autofill.BetterAuth.WebauthnResult.CheckoutOptIn";
CreateServerCard(kTestGUID, kTestNumber);
const CreditCard* card =
personal_data().payments_data_manager().GetCreditCardByGUID(kTestGUID);
GetFIDOAuthenticator()->SetUserVerifiable(true);
SetCreditCardFIDOAuthEnabled(false);
FetchCreditCardAndCompleteRiskBasedAuthIfAvailable(card);
InvokeUnmaskDetailsTimeout();
WaitForCallbacks();
// For Android, set `test_fido_request_options_type` to valid to mock user
// checking the opt-in checkbox and ensuring GetRealPan returns
// RequestOptions.
EXPECT_TRUE(GetRealPanForCVCAuth(PaymentsRpcResult::kSuccess, kTestNumber,
TestFidoRequestOptionsType::kValid));
WaitForCallbacks();
// Check current flow to ensure CreditCardFidoAuthenticator::Authorize is
// called and correct flow is set.
EXPECT_EQ(CreditCardFidoAuthenticator::Flow::OPT_IN_WITH_CHALLENGE_FLOW,
GetFIDOAuthenticator()->current_flow());
// Ensure that the form is not filled yet (OnCreditCardFetched is not called).
EXPECT_EQ(accessor_->number(), std::u16string());
EXPECT_EQ(accessor_->cvc(), std::u16string());
// Mock user response.
TestCreditCardFidoAuthenticator::GetAssertion(GetFIDOAuthenticator(),
/*did_succeed=*/true);
// Ensure that the form is filled after user verification (OnCreditCardFetched
// is called).
EXPECT_EQ(kTestNumber16, accessor_->number());
EXPECT_EQ(kTestCvc16, accessor_->cvc());
// Mock OptChange payments call.
OptChange(PaymentsRpcResult::kSuccess,
/*user_is_opted_in=*/true);
EXPECT_EQ(kGooglePaymentsRpid, GetFIDOAuthenticator()->GetRelyingPartyId());
EXPECT_EQ(kTestChallenge,
BytesToBase64(GetFIDOAuthenticator()->GetChallenge()));
EXPECT_TRUE(GetFIDOAuthenticator()->IsUserOptedIn());
histogram_tester.ExpectUniqueSample(
histogram_name, autofill_metrics::WebauthnResultMetric::kSuccess, 1);
}
// Ensures that the card is filled into the form if the request options returned
// are invalid when the user opts in through the checkbox.
TEST_P(CreditCardAccessManagerAuthFlowTest,
FIDOOptInFailure_InvalidResponseRequestOptions) {
CreateServerCard(kTestGUID, kTestNumber);
const CreditCard* card =
personal_data().payments_data_manager().GetCreditCardByGUID(kTestGUID);
GetFIDOAuthenticator()->SetUserVerifiable(true);
SetCreditCardFIDOAuthEnabled(false);
FetchCreditCardAndCompleteRiskBasedAuthIfAvailable(card);
InvokeUnmaskDetailsTimeout();
WaitForCallbacks();
// Set the test request options returned to invalid to mock the user checking
// the checkbox, but invalid request options are returned from the server.
EXPECT_TRUE(GetRealPanForCVCAuth(PaymentsRpcResult::kSuccess, kTestNumber,
TestFidoRequestOptionsType::kInvalid));
WaitForCallbacks();
// Ensure that the form is filled because the request options returned from
// the response were invalid.
EXPECT_EQ(accessor_->number(), kTestNumber16);
EXPECT_EQ(accessor_->cvc(), kTestCvc16);
}
// Ensures that the failed user verification disallows enrollment.
TEST_P(CreditCardAccessManagerAuthFlowTest, FIDOOptInUserVerificationFailure) {
base::HistogramTester histogram_tester;
std::string histogram_name =
"Autofill.BetterAuth.WebauthnResult.CheckoutOptIn";
CreateServerCard(kTestGUID, kTestNumber);
const CreditCard* card =
personal_data().payments_data_manager().GetCreditCardByGUID(kTestGUID);
GetFIDOAuthenticator()->SetUserVerifiable(true);
SetCreditCardFIDOAuthEnabled(false);
FetchCreditCardAndCompleteRiskBasedAuthIfAvailable(card);
InvokeUnmaskDetailsTimeout();
WaitForCallbacks();
// For Android, set `test_fido_request_options_type` to valid to mock user
// checking the opt-in checkbox and ensuring GetRealPan returns
// RequestOptions.
EXPECT_TRUE(GetRealPanForCVCAuth(PaymentsRpcResult::kSuccess, kTestNumber,
TestFidoRequestOptionsType::kValid));
// Check current flow to ensure CreditCardFidoAuthenticator::Authorize is
// called and correct flow is set.
EXPECT_EQ(CreditCardFidoAuthenticator::Flow::OPT_IN_WITH_CHALLENGE_FLOW,
GetFIDOAuthenticator()->current_flow());
// Ensure that the form is not filled yet (OnCreditCardFetched is not called).
EXPECT_EQ(accessor_->number(), std::u16string());
EXPECT_EQ(accessor_->cvc(), std::u16string());
// Mock GetAssertion failure.
TestCreditCardFidoAuthenticator::GetAssertion(GetFIDOAuthenticator(),
/*did_succeed=*/false);
// Ensure that form is still filled even if user verification fails
// (OnCreditCardFetched is called). Note that this is different behavior than
// registering a new card.
EXPECT_EQ(kTestNumber16, accessor_->number());
EXPECT_EQ(kTestCvc16, accessor_->cvc());
EXPECT_FALSE(GetFIDOAuthenticator()->IsUserOptedIn());
histogram_tester.ExpectUniqueSample(
histogram_name, autofill_metrics::WebauthnResultMetric::kNotAllowedError,
1);
}
// Ensures that enrollment does not happen if the server returns a failure.
TEST_P(CreditCardAccessManagerAuthFlowTest, FIDOOptInServerFailure) {
CreateServerCard(kTestGUID, kTestNumber);
const CreditCard* card =
personal_data().payments_data_manager().GetCreditCardByGUID(kTestGUID);
GetFIDOAuthenticator()->SetUserVerifiable(true);
SetCreditCardFIDOAuthEnabled(false);
FetchCreditCardAndCompleteRiskBasedAuthIfAvailable(card);
InvokeUnmaskDetailsTimeout();
WaitForCallbacks();
// For Android, set `test_fido_request_options_type` to valid to mock user
// checking the opt-in checkbox and ensuring GetRealPan returns
// RequestOptions.
EXPECT_TRUE(GetRealPanForCVCAuth(PaymentsRpcResult::kSuccess, kTestNumber,
TestFidoRequestOptionsType::kValid));
// Check current flow to ensure CreditCardFidoAuthenticator::Authorize is
// called and correct flow is set.
EXPECT_EQ(CreditCardFidoAuthenticator::Flow::OPT_IN_WITH_CHALLENGE_FLOW,
GetFIDOAuthenticator()->current_flow());
// Ensure that the form is not filled yet (OnCreditCardFetched is not called).
EXPECT_EQ(accessor_->number(), std::u16string());
EXPECT_EQ(accessor_->cvc(), std::u16string());
// Mock user response and OptChange payments call.
TestCreditCardFidoAuthenticator::GetAssertion(GetFIDOAuthenticator(),
/*did_succeed=*/true);
// Ensure that the form is filled after user verification (OnCreditCardFetched
// is called).
EXPECT_EQ(kTestNumber16, accessor_->number());
EXPECT_EQ(kTestCvc16, accessor_->cvc());
OptChange(PaymentsRpcResult::kPermanentFailure, false);
EXPECT_FALSE(GetFIDOAuthenticator()->IsUserOptedIn());
}
// Ensures that enrollment does not happen if user unchecking the opt-in
// checkbox.
TEST_P(CreditCardAccessManagerAuthFlowTest, FIDOOptIn_CheckboxDeclined) {
CreateServerCard(kTestGUID, kTestNumber);
const CreditCard* card =
personal_data().payments_data_manager().GetCreditCardByGUID(kTestGUID);
GetFIDOAuthenticator()->SetUserVerifiable(true);
SetCreditCardFIDOAuthEnabled(false);
FetchCreditCardAndCompleteRiskBasedAuthIfAvailable(card);
InvokeUnmaskDetailsTimeout();
WaitForCallbacks();
// For Android, set `test_fido_request_options_type` to not present to mock
// user unchecking the opt-in checkbox resulting in GetRealPan not returning
// request options.
EXPECT_TRUE(GetRealPanForCVCAuth(PaymentsRpcResult::kSuccess, kTestNumber,
TestFidoRequestOptionsType::kNotPresent));
// Ensure that form is filled (OnCreditCardFetched is called).
EXPECT_EQ(kTestNumber16, accessor_->number());
EXPECT_EQ(kTestCvc16, accessor_->cvc());
// Check current flow to ensure CreditCardFidoAuthenticator::Authorize is
// never called.
EXPECT_EQ(CreditCardFidoAuthenticator::Flow::NONE_FLOW,
GetFIDOAuthenticator()->current_flow());
EXPECT_FALSE(GetFIDOAuthenticator()->IsUserOptedIn());
}
// Ensures that opting-in through settings page on Android successfully sends an
// opt-in request the next time the user downstreams a card.
TEST_P(CreditCardAccessManagerAuthFlowTest,
FIDOSettingsPageOptInSuccess_Android) {
CreateServerCard(kTestGUID, kTestNumber);
const CreditCard* card =
personal_data().payments_data_manager().GetCreditCardByGUID(kTestGUID);
GetFIDOAuthenticator()->SetUserVerifiable(true);
// Setting the local opt-in state as true and implying that Payments servers
// has the opt-in state to false - this shows the user opted-in through the
// settings page.
SetCreditCardFIDOAuthEnabled(true);
payments_network_interface().AllowFidoRegistration(true);
payments_network_interface().ShouldReturnUnmaskDetailsImmediately(true);
credit_card_access_manager().PrepareToFetchCreditCard();
FetchCreditCardAndCompleteRiskBasedAuthIfAvailable(card);
InvokeUnmaskDetailsTimeout();
WaitForCallbacks();
MockUserResponseForCvcAuth(kTestCvc16, /*enable_fido=*/false);
// Although the checkbox was hidden and |enable_fido_auth| was set to false in
// the user request, because of the previous opt-in intention, the client must
// request to opt-in.
EXPECT_TRUE(payments_network_interface()
.unmask_request()
->user_response.enable_fido_auth);
}
#else // BUILDFLAG(IS_ANDROID)
// Ensures that the WebAuthn enrollment prompt is invoked after user opts in. In
// this case, the user is not yet enrolled server-side, and thus receives
// |creation_options|.
TEST_P(CreditCardAccessManagerAuthFlowTest,
FIDOEnrollmentSuccess_CreationOptions_Desktop) {
base::HistogramTester histogram_tester;
std::string webauthn_result_histogram_name =
"Autofill.BetterAuth.WebauthnResult.CheckoutOptIn";
std::string opt_in_histogram_name =
"Autofill.BetterAuth.OptInCalled.FromCheckoutFlow";
std::string promo_shown_histogram_name =
"Autofill.BetterAuth.OptInPromoShown.FromCheckoutFlow";
std::string promo_user_decision_histogram_name =
"Autofill.BetterAuth.OptInPromoUserDecision.FromCheckoutFlow";
ClearStrikes();
CreateServerCard(kTestGUID, kTestNumber);
const CreditCard* card =
personal_data().payments_data_manager().GetCreditCardByGUID(kTestGUID);
GetFIDOAuthenticator()->SetUserVerifiable(true);
SetCreditCardFIDOAuthEnabled(false);
payments_network_interface().AllowFidoRegistration(true);
credit_card_access_manager().PrepareToFetchCreditCard();
FetchCreditCardAndCompleteRiskBasedAuthIfAvailable(card);
WaitForCallbacks();
// Mock user and payments response.
EXPECT_TRUE(GetRealPanForCVCAuth(PaymentsRpcResult::kSuccess, kTestNumber));
AcceptWebauthnOfferDialog(/*did_accept=*/true);
OptChange(PaymentsRpcResult::kSuccess,
/*user_is_opted_in=*/false,
/*include_creation_options=*/true);
// Mock user response and OptChange payments call.
EXPECT_EQ(CreditCardFidoAuthenticator::Flow::OPT_IN_WITH_CHALLENGE_FLOW,
GetFIDOAuthenticator()->current_flow());
TestCreditCardFidoAuthenticator::MakeCredential(GetFIDOAuthenticator(),
/*did_succeed=*/true);
OptChange(PaymentsRpcResult::kSuccess,
/*user_is_opted_in=*/true);
EXPECT_EQ(kGooglePaymentsRpid, GetFIDOAuthenticator()->GetRelyingPartyId());
EXPECT_EQ(kTestChallenge,
BytesToBase64(GetFIDOAuthenticator()->GetChallenge()));
EXPECT_TRUE(GetFIDOAuthenticator()->IsUserOptedIn());
EXPECT_EQ(0, GetStrikes());
histogram_tester.ExpectUniqueSample(
webauthn_result_histogram_name,
autofill_metrics::WebauthnResultMetric::kSuccess, 1);
histogram_tester.ExpectTotalCount(opt_in_histogram_name, 2);
histogram_tester.ExpectBucketCount(
opt_in_histogram_name,
autofill_metrics::WebauthnOptInParameters::kFetchingChallenge, 1);
histogram_tester.ExpectBucketCount(
opt_in_histogram_name,
autofill_metrics::WebauthnOptInParameters::kWithCreationChallenge, 1);
histogram_tester.ExpectTotalCount(promo_shown_histogram_name, 1);
histogram_tester.ExpectUniqueSample(
promo_user_decision_histogram_name,
autofill_metrics::WebauthnOptInPromoUserDecisionMetric::kAccepted, 1);
}
// Ensures that the correct number of strikes are added when the user declines
// the WebAuthn offer.
TEST_P(CreditCardAccessManagerAuthFlowTest,
FIDOEnrollment_OfferDeclined_Desktop) {
base::HistogramTester histogram_tester;
std::string promo_shown_histogram_name =
"Autofill.BetterAuth.OptInPromoShown.FromCheckoutFlow";
std::string promo_user_decision_histogram_name =
"Autofill.BetterAuth.OptInPromoUserDecision.FromCheckoutFlow";
ClearStrikes();
CreateServerCard(kTestGUID, kTestNumber);
const CreditCard* card =
personal_data().payments_data_manager().GetCreditCardByGUID(kTestGUID);
GetFIDOAuthenticator()->SetUserVerifiable(true);
SetCreditCardFIDOAuthEnabled(false);
payments_network_interface().AllowFidoRegistration(true);
credit_card_access_manager().PrepareToFetchCreditCard();
FetchCreditCardAndCompleteRiskBasedAuthIfAvailable(card);
WaitForCallbacks();
// Mock user and payments response.
EXPECT_TRUE(GetRealPanForCVCAuth(PaymentsRpcResult::kSuccess, kTestNumber));
AcceptWebauthnOfferDialog(/*did_accept=*/false);
EXPECT_EQ(
FidoAuthenticationStrikeDatabase::kStrikesToAddWhenOptInOfferDeclined,
GetStrikes());
histogram_tester.ExpectTotalCount(promo_shown_histogram_name, 1);
histogram_tester.ExpectUniqueSample(
promo_user_decision_histogram_name,
autofill_metrics::WebauthnOptInPromoUserDecisionMetric::
kDeclinedImmediately,
1);
}
// Ensures that the correct number of strikes are added when the user declines
// the WebAuthn offer.
TEST_P(CreditCardAccessManagerAuthFlowTest,
FIDOEnrollment_OfferDeclinedAfterAccepting_Desktop) {
base::HistogramTester histogram_tester;
std::string promo_shown_histogram_name =
"Autofill.BetterAuth.OptInPromoShown.FromCheckoutFlow";
std::string promo_user_decision_histogram_name =
"Autofill.BetterAuth.OptInPromoUserDecision.FromCheckoutFlow";
ClearStrikes();
CreateServerCard(kTestGUID, kTestNumber);
const CreditCard* card =
personal_data().payments_data_manager().GetCreditCardByGUID(kTestGUID);
GetFIDOAuthenticator()->SetUserVerifiable(true);
SetCreditCardFIDOAuthEnabled(false);
payments_network_interface().AllowFidoRegistration(true);
credit_card_access_manager().PrepareToFetchCreditCard();
FetchCreditCardAndCompleteRiskBasedAuthIfAvailable(card);
WaitForCallbacks();
// Mock user and payments response.
EXPECT_TRUE(GetRealPanForCVCAuth(PaymentsRpcResult::kSuccess, kTestNumber));
AcceptWebauthnOfferDialog(/*did_accept=*/true);
AcceptWebauthnOfferDialog(/*did_accept=*/false);
EXPECT_EQ(
FidoAuthenticationStrikeDatabase::kStrikesToAddWhenOptInOfferDeclined,
GetStrikes());
histogram_tester.ExpectTotalCount(promo_shown_histogram_name, 1);
histogram_tester.ExpectUniqueSample(
promo_user_decision_histogram_name,
autofill_metrics::WebauthnOptInPromoUserDecisionMetric::
kDeclinedAfterAccepting,
1);
}
// Ensures that the correct number of strikes are added when the user fails to
// complete user-verification for an opt-in attempt.
TEST_P(CreditCardAccessManagerAuthFlowTest,
FIDOEnrollment_UserVerificationFailed_Desktop) {
base::HistogramTester histogram_tester;
std::string webauthn_result_histogram_name =
"Autofill.BetterAuth.WebauthnResult.CheckoutOptIn";
std::string opt_in_histogram_name =
"Autofill.BetterAuth.OptInCalled.FromCheckoutFlow";
ClearStrikes();
CreateServerCard(kTestGUID, kTestNumber);
const CreditCard* card =
personal_data().payments_data_manager().GetCreditCardByGUID(kTestGUID);
GetFIDOAuthenticator()->SetUserVerifiable(true);
SetCreditCardFIDOAuthEnabled(false);
payments_network_interface().AllowFidoRegistration(true);
credit_card_access_manager().PrepareToFetchCreditCard();
FetchCreditCardAndCompleteRiskBasedAuthIfAvailable(card);
InvokeUnmaskDetailsTimeout();
WaitForCallbacks();
// Mock user and payments response.
EXPECT_TRUE(GetRealPanForCVCAuth(PaymentsRpcResult::kSuccess, kTestNumber));
WaitForCallbacks();
AcceptWebauthnOfferDialog(/*did_accept=*/true);
OptChange(PaymentsRpcResult::kSuccess,
/*user_is_opted_in=*/false,
/*include_creation_options=*/true);
// Mock user response.
TestCreditCardFidoAuthenticator::MakeCredential(GetFIDOAuthenticator(),
/*did_succeed=*/false);
EXPECT_EQ(FidoAuthenticationStrikeDatabase::
kStrikesToAddWhenUserVerificationFailsOnOptInAttempt,
GetStrikes());
histogram_tester.ExpectUniqueSample(
webauthn_result_histogram_name,
autofill_metrics::WebauthnResultMetric::kNotAllowedError, 1);
histogram_tester.ExpectUniqueSample(
opt_in_histogram_name,
autofill_metrics::WebauthnOptInParameters::kFetchingChallenge, 1);
}
// Ensures that the WebAuthn enrollment prompt is invoked after user opts in. In
// this case, the user is already enrolled server-side, and thus receives
// |request_options|.
TEST_P(CreditCardAccessManagerAuthFlowTest,
FIDOEnrollmentSuccess_RequestOptions_Desktop) {
base::HistogramTester histogram_tester;
std::string webauthn_result_histogram_name =
"Autofill.BetterAuth.WebauthnResult.CheckoutOptIn";
std::string opt_in_histogram_name =
"Autofill.BetterAuth.OptInCalled.FromCheckoutFlow";
CreateServerCard(kTestGUID, kTestNumber);
const CreditCard* card =
personal_data().payments_data_manager().GetCreditCardByGUID(kTestGUID);
GetFIDOAuthenticator()->SetUserVerifiable(true);
SetCreditCardFIDOAuthEnabled(false);
payments_network_interface().AllowFidoRegistration(true);
credit_card_access_manager().PrepareToFetchCreditCard();
FetchCreditCardAndCompleteRiskBasedAuthIfAvailable(card);
WaitForCallbacks();
// Mock user and payments response.
EXPECT_TRUE(GetRealPanForCVCAuth(PaymentsRpcResult::kSuccess, kTestNumber));
WaitForCallbacks();
AcceptWebauthnOfferDialog(/*did_accept=*/true);
OptChange(PaymentsRpcResult::kSuccess,
/*user_is_opted_in=*/false,
/*include_creation_options=*/false,
/*include_request_options=*/true);
// Mock user response and OptChange payments call.
EXPECT_EQ(CreditCardFidoAuthenticator::Flow::OPT_IN_WITH_CHALLENGE_FLOW,
GetFIDOAuthenticator()->current_flow());
TestCreditCardFidoAuthenticator::GetAssertion(GetFIDOAuthenticator(),
/*did_succeed=*/true);
OptChange(PaymentsRpcResult::kSuccess,
/*user_is_opted_in=*/true);
EXPECT_EQ(kGooglePaymentsRpid, GetFIDOAuthenticator()->GetRelyingPartyId());
EXPECT_EQ(kTestChallenge,
BytesToBase64(GetFIDOAuthenticator()->GetChallenge()));
EXPECT_TRUE(GetFIDOAuthenticator()->IsUserOptedIn());
histogram_tester.ExpectUniqueSample(
webauthn_result_histogram_name,
autofill_metrics::WebauthnResultMetric::kSuccess, 1);
histogram_tester.ExpectTotalCount(opt_in_histogram_name, 2);
histogram_tester.ExpectBucketCount(
opt_in_histogram_name,
autofill_metrics::WebauthnOptInParameters::kFetchingChallenge, 1);
histogram_tester.ExpectBucketCount(
opt_in_histogram_name,
autofill_metrics::WebauthnOptInParameters::kWithRequestChallenge, 1);
}
TEST_F(CreditCardAccessManagerTest, SettingsPage_OptOut) {
base::HistogramTester histogram_tester;
GetFIDOAuthenticator()->SetUserVerifiable(true);
SetCreditCardFIDOAuthEnabled(true);
EXPECT_TRUE(IsCreditCardFIDOAuthEnabled());
credit_card_access_manager().OnSettingsPageFIDOAuthToggled(false);
EXPECT_TRUE(GetFIDOAuthenticator()->IsOptOutCalled());
OptChange(PaymentsRpcResult::kSuccess,
/*user_is_opted_in=*/false);
EXPECT_FALSE(IsCreditCardFIDOAuthEnabled());
}
#endif // BUILDFLAG(IS_ANDROID)
// Ensure that when unmask detail response is delayed, we will automatically
// fall back to CVC even if local pref and Payments mismatch.
TEST_P(CreditCardAccessManagerAuthFlowTest,
IntentToOptOut_DelayedUnmaskDetailsResponse) {
if (IsMaskedServerCardRiskBasedAuthEnabled()) {
GTEST_SKIP() << "For risk based authentication, whether fallback to FIDO "
"or CVC flow doesn't rely on the platform call";
}
base::HistogramTester histogram_tester;
// Setting up a FIDO-enabled user with a server card.
CreateServerCard(kTestGUID, kTestNumber);
const CreditCard* card =
personal_data().payments_data_manager().GetCreditCardByGUID(kTestGUID);
// The user is FIDO-enabled from Payments.
GetFIDOAuthenticator()->SetUserVerifiable(true);
SetCreditCardFIDOAuthEnabled(true);
payments_network_interface().AddFidoEligibleCard(
card->server_id(), kCredentialId, kGooglePaymentsRpid);
// Mock the user manually opt-out from Settings page, and Payments did not
// update user status in time. The mismatch will set user INTENT_TO_OPT_OUT.
SetCreditCardFIDOAuthEnabled(/*enabled=*/false);
// Delay the UnmaskDetailsResponse so that we can't discover the mismatch,
// which will use local pref and fall back to CVC.
payments_network_interface().ShouldReturnUnmaskDetailsImmediately(false);
credit_card_access_manager().PrepareToFetchCreditCard();
FetchCreditCard(card);
// Ensure the auth flow type is CVC because no unmask detail response is
// returned and local pref denotes that user is opted out.
EXPECT_EQ(GetUnmaskAuthFlowType(), UnmaskAuthFlowType::kCvc);
// Also ensure that since local pref is disabled, we will directly fall back
// to CVC instead of falling back after time out. Ensure that
// CardChosenBeforePreflightCallReturned is logged to opted-out histogram.
histogram_tester.ExpectUniqueSample(
"Autofill.BetterAuth.UserPerceivedLatencyOnCardSelection.OptedOut",
autofill_metrics::PreflightCallEvent::
kCardChosenBeforePreflightCallReturned,
1);
// No bucket count for OptIn TimedOutCvcFallback.
histogram_tester.ExpectTotalCount(
"Autofill.BetterAuth.UserPerceivedLatencyOnCardSelection.OptedIn."
"TimedOutCvcFallback",
0);
EXPECT_TRUE(GetRealPanForCVCAuth(PaymentsRpcResult::kSuccess, kTestNumber));
// Since no unmask detail returned, we can't discover the pref mismatch, we
// won't call opt out and local pref is unchanged.
EXPECT_FALSE(GetFIDOAuthenticator()->IsOptOutCalled());
EXPECT_FALSE(IsCreditCardFIDOAuthEnabled());
}
TEST_P(CreditCardAccessManagerAuthFlowTest,
IntentToOptOut_OptOutAfterUnmaskSucceeds) {
// Setting up a FIDO-enabled user with a server card.
CreateServerCard(kTestGUID, kTestNumber);
const CreditCard* card =
personal_data().payments_data_manager().GetCreditCardByGUID(kTestGUID);
// The user is FIDO-enabled from Payments.
GetFIDOAuthenticator()->SetUserVerifiable(true);
SetCreditCardFIDOAuthEnabled(true);
payments_network_interface().AddFidoEligibleCard(
card->server_id(), kCredentialId, kGooglePaymentsRpid);
// Mock the user manually opt-out from Settings page, and Payments did not
// update user status in time. The mismatch will set user INTENT_TO_OPT_OUT.
SetCreditCardFIDOAuthEnabled(/*enabled=*/false);
credit_card_access_manager().PrepareToFetchCreditCard();
FetchCreditCardAndCompleteRiskBasedAuthIfAvailable(card);
// Ensure that the local pref is still unchanged after unmask detail returns.
EXPECT_FALSE(IsCreditCardFIDOAuthEnabled());
// Also ensure the auth flow type is CVC because the local pref and payments
// mismatch indicates that user intended to opt out.
EXPECT_EQ(GetUnmaskAuthFlowType(), UnmaskAuthFlowType::kCvc);
// Mock cvc auth success.
EXPECT_TRUE(GetRealPanForCVCAuth(PaymentsRpcResult::kSuccess, kTestNumber));
WaitForCallbacks();
// Ensure calling opt out after a successful cvc auth.
EXPECT_TRUE(GetFIDOAuthenticator()->IsOptOutCalled());
// Mock opt out success response. Local pref is consistent with payments.
OptChange(PaymentsRpcResult::kSuccess,
/*user_is_opted_in=*/false);
EXPECT_FALSE(IsCreditCardFIDOAuthEnabled());
}
TEST_P(CreditCardAccessManagerAuthFlowTest,
IntentToOptOut_OptOutAfterUnmaskFails) {
// Setting up a FIDO-enabled user with a server card.
CreateServerCard(kTestGUID, kTestNumber);
const CreditCard* card =
personal_data().payments_data_manager().GetCreditCardByGUID(kTestGUID);
// The user is FIDO-enabled from Payments.
GetFIDOAuthenticator()->SetUserVerifiable(true);
SetCreditCardFIDOAuthEnabled(true);
payments_network_interface().AddFidoEligibleCard(
card->server_id(), kCredentialId, kGooglePaymentsRpid);
// Mock the user manually opt-out from Settings page, and Payments did not
// update user status in time. The mismatch will set user INTENT_TO_OPT_OUT.
SetCreditCardFIDOAuthEnabled(/*enabled=*/false);
credit_card_access_manager().PrepareToFetchCreditCard();
FetchCreditCardAndCompleteRiskBasedAuthIfAvailable(card);
// Ensure that the local pref is still unchanged after unmask detail returns.
EXPECT_FALSE(IsCreditCardFIDOAuthEnabled());
// Ensure the auth flow type is CVC because the local pref and payments
// mismatch indicates that user intended to opt out.
EXPECT_EQ(GetUnmaskAuthFlowType(), UnmaskAuthFlowType::kCvc);
// Mock cvc auth failure.
EXPECT_TRUE(GetRealPanForCVCAuth(PaymentsRpcResult::kPermanentFailure,
std::string()));
WaitForCallbacks();
// Ensure calling opt out after cvc auth failure.
EXPECT_TRUE(GetFIDOAuthenticator()->IsOptOutCalled());
// Mock opt out success. Local pref is consistent with payments.
OptChange(PaymentsRpcResult::kSuccess,
/*user_is_opted_in=*/false);
EXPECT_FALSE(IsCreditCardFIDOAuthEnabled());
}
TEST_P(CreditCardAccessManagerAuthFlowTest, IntentToOptOut_OptOutFailure) {
// Setting up a FIDO-enabled user with a server card.
CreateServerCard(kTestGUID, kTestNumber);
const CreditCard* card =
personal_data().payments_data_manager().GetCreditCardByGUID(kTestGUID);
// The user is FIDO-enabled from Payments.
GetFIDOAuthenticator()->SetUserVerifiable(true);
SetCreditCardFIDOAuthEnabled(true);
payments_network_interface().AddFidoEligibleCard(
card->server_id(), kCredentialId, kGooglePaymentsRpid);
// Mock the user manually opt-out from Settings page, and Payments did not
// update user status in time. The mismatch will set user INTENT_TO_OPT_OUT.
SetCreditCardFIDOAuthEnabled(/*enabled=*/false);
credit_card_access_manager().PrepareToFetchCreditCard();
FetchCreditCardAndCompleteRiskBasedAuthIfAvailable(card);
// Ensure that the local pref is still unchanged after unmask detail returns.
EXPECT_FALSE(IsCreditCardFIDOAuthEnabled());
// Also ensure the auth flow type is CVC because the local pref and payments
// mismatch indicates that user intended to opt out.
EXPECT_EQ(GetUnmaskAuthFlowType(), UnmaskAuthFlowType::kCvc);
// Mock cvc auth success.
EXPECT_TRUE(GetRealPanForCVCAuth(PaymentsRpcResult::kSuccess, kTestNumber));
WaitForCallbacks();
// Mock payments opt out failure. Local pref should be unchanged.
OptChange(PaymentsRpcResult::kPermanentFailure, false);
EXPECT_FALSE(IsCreditCardFIDOAuthEnabled());
}
// TODO(crbug.com/40253866): Extend the FIDOAuthOptChange tests to more
// use-cases.
TEST_F(CreditCardAccessManagerTest, FIDOAuthOptChange_OptOut) {
credit_card_access_manager().FIDOAuthOptChange(/*opt_in=*/false);
ASSERT_TRUE(fido_authenticator().IsOptOutCalled());
}
TEST_F(CreditCardAccessManagerTest, FIDOAuthOptChange_OptOut_OffTheRecord) {
autofill_client_.set_is_off_the_record(true);
credit_card_access_manager().FIDOAuthOptChange(/*opt_in=*/false);
ASSERT_FALSE(fido_authenticator().IsOptOutCalled());
}
// TODO(crbug.com/40707930) Debug issues and re-enable this test on MacOS.
#if !BUILDFLAG(IS_APPLE)
// Ensures that PrepareToFetchCreditCard() is properly rate limited.
TEST_F(CreditCardAccessManagerTest, PreflightCallRateLimited) {
// Create server card and set user as eligible for FIDO auth.
base::HistogramTester histogram_tester;
std::string preflight_call_metric =
"Autofill.BetterAuth.CardUnmaskPreflightCalledWithFidoOptInStatus";
ClearCards();
CreateServerCard(kTestGUID, kTestNumber);
GetFIDOAuthenticator()->SetUserVerifiable(true);
ResetFetchCreditCard();
// First call to PrepareToFetchCreditCard() should make RPC.
credit_card_access_manager().PrepareToFetchCreditCard();
histogram_tester.ExpectTotalCount(preflight_call_metric, 1);
// Calling PrepareToFetchCreditCard() without a prior preflight call should
// have set |can_fetch_unmask_details_| to false to prevent further ones.
EXPECT_FALSE(
test_api(credit_card_access_manager()).can_fetch_unmask_details());
// Any subsequent calls should not make a RPC.
credit_card_access_manager().PrepareToFetchCreditCard();
histogram_tester.ExpectTotalCount(preflight_call_metric, 1);
}
#endif // !BUILDFLAG(IS_APPLE)
#endif // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_ANDROID)
// Ensures that UnmaskAuthFlowEvents also log to a ".ServerCard" subhistogram
// when a masked server card is selected.
TEST_P(CreditCardAccessManagerAuthFlowTest,
UnmaskAuthFlowEvent_AlsoLogsServerCardSubhistogram) {
CreateServerCard(kTestGUID, kTestNumber);
const CreditCard* card =
personal_data().payments_data_manager().GetCreditCardByGUID(kTestGUID);
base::HistogramTester histogram_tester;
std::string flow_events_histogram_name =
"Autofill.BetterAuth.FlowEvents.Cvc.ServerCard";
PrepareToFetchCreditCardAndWaitForCallbacks();
FetchCreditCardAndCompleteRiskBasedAuthIfAvailable(card);
histogram_tester.ExpectUniqueSample(
flow_events_histogram_name,
CreditCardFormEventLogger::UnmaskAuthFlowEvent::kPromptShown, 1);
EXPECT_TRUE(GetRealPanForCVCAuth(PaymentsRpcResult::kSuccess, kTestNumber));
histogram_tester.ExpectBucketCount(
flow_events_histogram_name,
CreditCardFormEventLogger::UnmaskAuthFlowEvent::kPromptCompleted, 1);
}
// Ensures that |is_authentication_in_progress_| is set correctly.
TEST_P(CreditCardAccessManagerAuthFlowTest, AuthenticationInProgress) {
CreateServerCard(kTestGUID, kTestNumber);
const CreditCard* card =
personal_data().payments_data_manager().GetCreditCardByGUID(kTestGUID);
EXPECT_FALSE(IsAuthenticationInProgress());
FetchCreditCardAndCompleteRiskBasedAuthIfAvailable(card);
EXPECT_TRUE(IsAuthenticationInProgress());
EXPECT_TRUE(GetRealPanForCVCAuth(PaymentsRpcResult::kSuccess, kTestNumber));
EXPECT_FALSE(IsAuthenticationInProgress());
}
// Ensures that the use of |unmasked_card_cache_| is set and logged correctly.
TEST_F(CreditCardAccessManagerTest, FetchCreditCardUsesUnmaskedCardCache) {
base::HistogramTester histogram_tester;
CreateServerCard(kTestGUID, kTestNumber);
CreditCard masked_card =
*personal_data().payments_data_manager().GetCreditCardByGUID(kTestGUID);
// Mock out that the card has become unmasked and cached.
CreditCard unmasked_card = masked_card;
unmasked_card.set_record_type(CreditCard::RecordType::kFullServerCard);
credit_card_access_manager().CacheUnmaskedCardInfo(unmasked_card, kTestCvc16);
// Now fetch the masked credit card - this should use the cached unmasked
// version.
FetchCreditCard(&masked_card);
histogram_tester.ExpectUniqueSample(
"Autofill.ServerCardUnmask.ServerCard.Result.UnspecifiedFlowType",
autofill_metrics::ServerCardUnmaskResult::kLocalCacheHit, 1);
FetchCreditCard(&masked_card);
histogram_tester.ExpectUniqueSample(
"Autofill.ServerCardUnmask.ServerCard.Result.UnspecifiedFlowType",
autofill_metrics::ServerCardUnmaskResult::kLocalCacheHit, 2);
// Create a virtual card.
CreditCard virtual_card = CreditCard();
test::SetCreditCardInfo(&virtual_card, "Elvis Presley", kTestNumber,
test::NextMonth().c_str(), test::NextYear().c_str(),
"1");
virtual_card.set_record_type(CreditCard::RecordType::kVirtualCard);
credit_card_access_manager().CacheUnmaskedCardInfo(virtual_card, kTestCvc16);
// Mocks that user selects the virtual card option of the masked card.
masked_card.set_record_type(CreditCard::RecordType::kVirtualCard);
FetchCreditCard(&masked_card);
histogram_tester.ExpectUniqueSample(
"Autofill.ServerCardUnmask.VirtualCard.Result.UnspecifiedFlowType",
autofill_metrics::ServerCardUnmaskResult::kLocalCacheHit, 1);
}
TEST_F(CreditCardAccessManagerTest, GetCachedUnmaskedCards) {
// Assert that there are no cards cached initially.
EXPECT_EQ(0U, credit_card_access_manager().GetCachedUnmaskedCards().size());
const CreditCard* masked_server_card =
CreateServerCard(kTestGUID, kTestNumber, kTestServerId);
CreateServerCard(kTestGUID2, kTestNumber2, kTestServerId2);
// Add a card to the cache.
CreditCard unmasked_card = *masked_server_card;
unmasked_card.set_record_type(CreditCard::RecordType::kFullServerCard);
credit_card_access_manager().CacheUnmaskedCardInfo(unmasked_card, kTestCvc16);
// Verify that only the card added to the cache is returned.
ASSERT_EQ(1U, credit_card_access_manager().GetCachedUnmaskedCards().size());
EXPECT_EQ(unmasked_card,
credit_card_access_manager().GetCachedUnmaskedCards()[0]->card);
}
TEST_F(CreditCardAccessManagerTest, IsCardPresentInUnmaskedCache) {
const CreditCard* masked_server_card =
CreateServerCard(kTestGUID, kTestNumber, kTestServerId);
CreateServerCard(kTestGUID2, kTestNumber2, kTestServerId2);
// Add a card to the cache.
CreditCard unmasked_card = *masked_server_card;
unmasked_card.set_record_type(CreditCard::RecordType::kFullServerCard);
credit_card_access_manager().CacheUnmaskedCardInfo(unmasked_card, kTestCvc16);
// Verify that only one card is present in the cache.
EXPECT_TRUE(
credit_card_access_manager().IsCardPresentInUnmaskedCache(unmasked_card));
EXPECT_FALSE(credit_card_access_manager().IsCardPresentInUnmaskedCache(
*personal_data().payments_data_manager().GetCreditCardByGUID(
kTestGUID2)));
}
TEST_F(CreditCardAccessManagerTest, IsVirtualCardPresentInUnmaskedCache) {
const CreditCard* masked_server_card =
CreateServerCard(kTestGUID, kTestNumber, kTestServerId);
CreditCard virtual_card = *masked_server_card;
virtual_card.set_record_type(CreditCard::RecordType::kVirtualCard);
// Add the virtual card to the cache.
credit_card_access_manager().CacheUnmaskedCardInfo(virtual_card, kTestCvc16);
// Verify that the virtual card is present in the cache.
EXPECT_TRUE(
credit_card_access_manager().IsCardPresentInUnmaskedCache(virtual_card));
}
TEST_F(CreditCardAccessManagerTest,
RiskBasedVirtualCardUnmasking_Success_VirtualCards) {
base::HistogramTester histogram_tester;
#if BUILDFLAG(IS_ANDROID)
if (base::android::device_info::is_automotive()) {
GTEST_SKIP() << "This test should not run on automotive.";
}
#endif // BUILDFLAG(IS_ANDROID)
const CreditCard* masked_server_card =
CreateServerCard(kTestGUID, kTestNumber, kTestServerId);
CreditCard server_card = *masked_server_card;
server_card.set_record_type(CreditCard::RecordType::kVirtualCard);
FetchCreditCard(&server_card);
// This checks risk-based authentication flow is successfully invoked,
// because it is always the very first authentication flow in a VCN
// unmasking flow.
EXPECT_TRUE(autofill_client_.GetPaymentsAutofillClient()
->risk_based_authentication_invoked());
const CreditCard* virtual_card_enrolled_regular_card =
personal_data().payments_data_manager().GetCreditCardByGUID(kTestGUID);
CreditCard virtual_card = *virtual_card_enrolled_regular_card;
virtual_card.set_record_type(CreditCard::RecordType::kVirtualCard);
virtual_card.set_cvc(u"321");
credit_card_access_manager().OnRiskBasedAuthenticationResponseReceived(
CreditCardRiskBasedAuthenticator::RiskBasedAuthenticationResponse()
.with_result(CreditCardRiskBasedAuthenticator::
RiskBasedAuthenticationResponse::Result::
kNoAuthenticationRequired)
.with_card(virtual_card));
// Ensure the accessor received the correct response.
EXPECT_EQ(accessor_->number(), u"4234567890123456");
EXPECT_EQ(accessor_->cvc(), u"321");
EXPECT_EQ(accessor_->expiry_month(), base::UTF8ToUTF16(test::NextMonth()));
EXPECT_EQ(accessor_->expiry_year(), base::UTF8ToUTF16(test::NextYear()));
// There was no interactive authentication in this flow, so check that this
// is signaled correctly.
std::optional<NonInteractivePaymentMethodType> type =
test_api(*autofill_client_.GetFormDataImporter())
.payment_method_type_if_non_interactive_authentication_flow_completed();
EXPECT_THAT(type,
testing::Optional(NonInteractivePaymentMethodType::kVirtualCard));
// Expect the metrics are logged correctly.
histogram_tester.ExpectUniqueSample(
"Autofill.ServerCardUnmask.VirtualCard.Attempt", true, 1);
histogram_tester.ExpectUniqueSample(
"Autofill.ServerCardUnmask.VirtualCard.Result.RiskBased",
autofill_metrics::ServerCardUnmaskResult::kRiskBasedUnmasked, 1);
}
// Ensures the virtual card risk-based unmasking response is handled correctly
// and authentication is delegated to the OTP authenticator, when only the OTP
// challenge option is returned.
TEST_F(CreditCardAccessManagerTest,
RiskBasedVirtualCardUnmasking_AuthenticationRequired_OtpOnly) {
base::HistogramTester histogram_tester;
std::vector<CardUnmaskChallengeOption> challenge_options =
test::GetCardUnmaskChallengeOptions(
{CardUnmaskChallengeOptionType::kSmsOtp,
CardUnmaskChallengeOptionType::kEmailOtp});
MockCardUnmaskFlowUpToAuthenticationSelectionDialogAccepted(
/*fido_authenticator_is_user_opted_in=*/false,
/*is_user_verifiable=*/false, challenge_options,
/*selected_index=*/0, CreditCard::RecordType::kVirtualCard);
CreditCard card = test::GetVirtualCard();
credit_card_access_manager().OnOtpAuthenticationComplete(
CreditCardOtpAuthenticator::OtpAuthenticationResponse()
.with_result(CreditCardOtpAuthenticator::OtpAuthenticationResponse::
Result::kSuccess)
.with_card(&card)
.with_cvc(kTestCvc16));
// Expect that we did not signal that there was no interactive authentication.
EXPECT_FALSE(
test_api(*autofill_client_.GetFormDataImporter())
.payment_method_type_if_non_interactive_authentication_flow_completed()
.has_value());
// Expect accessor to successfully retrieve the CVC.
EXPECT_EQ(kTestCvc16, accessor_->cvc());
// Expect the metrics are logged correctly.
histogram_tester.ExpectUniqueSample(
"Autofill.ServerCardUnmask.VirtualCard.Attempt", true, 1);
histogram_tester.ExpectUniqueSample(
"Autofill.ServerCardUnmask.VirtualCard.Result.Otp",
autofill_metrics::ServerCardUnmaskResult::kAuthenticationUnmasked, 1);
}
// Ensures the virtual card risk-based unmasking response is handled correctly
// and authentication is delegated to the CVC authenticator, when only the CVC
// challenge option is returned.
TEST_F(CreditCardAccessManagerTest,
RiskBasedVirtualCardUnmasking_AuthenticationRequired_CvcOnly) {
base::HistogramTester histogram_tester;
std::vector<CardUnmaskChallengeOption> challenge_options =
test::GetCardUnmaskChallengeOptions(
{CardUnmaskChallengeOptionType::kCvc});
MockCardUnmaskFlowUpToAuthenticationSelectionDialogAccepted(
/*fido_authenticator_is_user_opted_in=*/false,
/*is_user_verifiable=*/false, challenge_options,
/*selected_index=*/0, CreditCard::RecordType::kVirtualCard);
CreditCard card = test::GetCreditCard();
credit_card_access_manager().OnCvcAuthenticationComplete(
CreditCardCvcAuthenticator::CvcAuthenticationResponse()
.with_did_succeed(true)
.with_card(&card)
.with_cvc(u"123"));
// Expect that we did not signal that there was no interactive authentication.
EXPECT_FALSE(
test_api(*autofill_client_.GetFormDataImporter())
.payment_method_type_if_non_interactive_authentication_flow_completed()
.has_value());
// Expect the metrics are logged correctly.
histogram_tester.ExpectUniqueSample(
"Autofill.ServerCardUnmask.VirtualCard.Attempt", true, 1);
// TODO(crbug.com/40240970): Add metrics checks for Virtual Card CVC auth
// result.
}
// Ensures the virtual card risk-based unmasking flow type is set to
// kThreeDomainSecure when only the 3DS challenge option is returned.
TEST_F(CreditCardAccessManagerTest,
RiskBasedVirtualCardUnmasking_Only3dsChallengeReturned) {
base::HistogramTester histogram_tester;
const CreditCard* masked_server_card =
CreateServerCard(kTestGUID, kTestNumber, kTestServerId);
CreditCard virtual_card = *masked_server_card;
virtual_card.set_record_type(CreditCard::RecordType::kVirtualCard);
FetchCreditCard(&virtual_card);
EXPECT_CALL(*static_cast<payments::MockPaymentsWindowManager*>(
autofill_client_.GetPaymentsAutofillClient()
->GetPaymentsWindowManager()),
InitVcn3dsAuthentication)
.Times(1)
.WillOnce([&](payments::PaymentsWindowManager::Vcn3dsContext context) {
EXPECT_EQ(context.context_token, "fake_context_token");
EXPECT_EQ(context.card, virtual_card);
EXPECT_EQ(context.challenge_option.type,
CardUnmaskChallengeOptionType::kThreeDomainSecure);
EXPECT_FALSE(context.user_consent_already_given);
});
CreditCardRiskBasedAuthenticator::RiskBasedAuthenticationResponse response;
response.context_token = "fake_context_token";
response.result = CreditCardRiskBasedAuthenticator::
RiskBasedAuthenticationResponse::Result::kAuthenticationRequired;
response.card_unmask_challenge_options = test::GetCardUnmaskChallengeOptions(
{CardUnmaskChallengeOptionType::kThreeDomainSecure});
credit_card_access_manager().OnRiskBasedAuthenticationResponseReceived(
response);
// If VCN 3DS is the only challenge option returned, verify that flow type is
// kThreeDomainSecure.
EXPECT_EQ(GetUnmaskAuthFlowType(), UnmaskAuthFlowType::kThreeDomainSecure);
histogram_tester.ExpectUniqueSample(
"Autofill.BetterAuth.FlowEvents.ThreeDomainSecure",
CreditCardFormEventLogger::UnmaskAuthFlowEvent::kPromptShown, 1);
}
// Ensures the `kCardInfoRetrievalEnrolledUnmaskProgressDialog` is set if
// `card_info_retrieval_enrollment_state` is enrolled.
TEST_F(CreditCardAccessManagerTest, CardInfoRetrievalEnrolledCardUnmasking) {
base::test::ScopedFeatureList scoped_feature_list{
features::kAutofillEnableFpanRiskBasedAuthentication};
base::HistogramTester histogram_tester;
CreditCard server_card = test::GetMaskedServerCard();
server_card.set_guid(kTestGUID);
server_card.set_card_info_retrieval_enrollment_state(
CreditCard::CardInfoRetrievalEnrollmentState::kRetrievalEnrolled);
personal_data().test_payments_data_manager().AddServerCreditCard(server_card);
const CreditCard* card =
personal_data().payments_data_manager().GetCreditCardByGUID(kTestGUID);
FetchCreditCard(card);
EXPECT_EQ(autofill_client_.GetPaymentsAutofillClient()
->autofill_progress_dialog_type(),
AutofillProgressDialogType::
kCardInfoRetrievalEnrolledUnmaskProgressDialog);
}
// Ensures the `kCardInfoRetrievalEnrolledUnmaskProgressDialog` is not set, even
// if `kAutofillEnableCardInfoRuntimeRetrieval` is enabled, but
// `card_info_retrieval_enrollment_state` is not enrolled.
TEST_F(CreditCardAccessManagerTest,
CardInfoRetrievalEnrolledCardUnmaskingDisabled) {
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitWithFeatures(
/*enabled_features=*/{features::kAutofillEnableCardInfoRuntimeRetrieval},
/*disabled_features=*/{
features::kAutofillEnableFpanRiskBasedAuthentication});
base::HistogramTester histogram_tester;
CreditCard server_card = test::GetMaskedServerCard();
server_card.set_guid(kTestGUID);
server_card.set_card_info_retrieval_enrollment_state(
CreditCard::CardInfoRetrievalEnrollmentState::kRetrievalUnspecified);
personal_data().test_payments_data_manager().AddServerCreditCard(server_card);
const CreditCard* card =
personal_data().payments_data_manager().GetCreditCardByGUID(kTestGUID);
FetchCreditCard(card);
// Ensures CreditCardRiskBasedAuthenticator::Authenticate is not invoked.
ASSERT_FALSE(autofill_client_.GetPaymentsAutofillClient()
->risk_based_authentication_invoked());
}
// Ensures the virtual card risk-based unmasking response is handled correctly
// and authentication is delegated to the correct authenticator when multiple
// challenge options are returned.
TEST_F(CreditCardAccessManagerTest,
RiskBasedVirtualCardUnmasking_AuthenticationRequired_OtpAndCvcAnd3ds) {
base::HistogramTester histogram_tester;
std::vector<CardUnmaskChallengeOption> challenge_options =
test::GetCardUnmaskChallengeOptions(
{CardUnmaskChallengeOptionType::kSmsOtp,
CardUnmaskChallengeOptionType::kCvc,
CardUnmaskChallengeOptionType::kThreeDomainSecure});
for (size_t selected_index = 0; selected_index < challenge_options.size();
selected_index++) {
MockCardUnmaskFlowUpToAuthenticationSelectionDialogAccepted(
/*fido_authenticator_is_user_opted_in=*/false,
/*is_user_verifiable=*/false, challenge_options, selected_index,
CreditCard::RecordType::kVirtualCard);
switch (challenge_options[selected_index].type) {
case CardUnmaskChallengeOptionType::kSmsOtp: {
CreditCard card = test::GetVirtualCard();
credit_card_access_manager().OnOtpAuthenticationComplete(
CreditCardOtpAuthenticator::OtpAuthenticationResponse()
.with_result(CreditCardOtpAuthenticator::
OtpAuthenticationResponse::Result::kSuccess)
.with_card(&card)
.with_cvc(u"123"));
break;
}
case CardUnmaskChallengeOptionType::kCvc: {
CreditCard card = test::GetVirtualCard();
credit_card_access_manager().OnCvcAuthenticationComplete(
CreditCardCvcAuthenticator::CvcAuthenticationResponse()
.with_did_succeed(true)
.with_card(&card)
.with_cvc(u"123"));
break;
}
case CardUnmaskChallengeOptionType::kThreeDomainSecure:
// VCN 3DS is one of the challenge options returned in the challenge
// selection dialog, and user selected the 3DS challenge option. Verify
// that flow type is kThreeDomainSecureConsentAlreadyGiven.
EXPECT_EQ(GetUnmaskAuthFlowType(),
UnmaskAuthFlowType::kThreeDomainSecureConsentAlreadyGiven);
break;
case CardUnmaskChallengeOptionType::kEmailOtp:
case CardUnmaskChallengeOptionType::kUnknownType:
NOTREACHED();
}
}
// Expect that we did not signal that there was no interactive authentication.
EXPECT_FALSE(
test_api(*autofill_client_.GetFormDataImporter())
.payment_method_type_if_non_interactive_authentication_flow_completed()
.has_value());
// Expect the metrics are logged correctly.
histogram_tester.ExpectUniqueSample(
"Autofill.ServerCardUnmask.VirtualCard.Attempt", true, 3);
histogram_tester.ExpectUniqueSample(
"Autofill.ServerCardUnmask.VirtualCard.Result.Otp",
autofill_metrics::ServerCardUnmaskResult::kAuthenticationUnmasked, 1);
// TODO(crbug.com/40240970): Add metrics checks for Virtual Card CVC auth
// result.
}
#if !BUILDFLAG(IS_IOS)
TEST_F(
CreditCardAccessManagerTest,
RiskBasedVirtualCardUnmasking_CreditCardAccessManagerReset_TriggersOtpAuthenticatorResetOnFlowCancelled) {
std::vector<CardUnmaskChallengeOption> challenge_options =
test::GetCardUnmaskChallengeOptions(
{CardUnmaskChallengeOptionType::kSmsOtp});
MockCardUnmaskFlowUpToAuthenticationSelectionDialogAccepted(
/*fido_authenticator_is_user_opted_in=*/false,
/*is_user_verifiable=*/false, challenge_options,
/*selected_index=*/0, CreditCard::RecordType::kVirtualCard);
// This check already happens in
// MockCardUnmaskFlowUpToAuthenticationSelectionDialogAccepted(), but double
// checking here helps show this test works correctly.
EXPECT_TRUE(otp_authenticator_->on_challenge_option_selected_invoked());
test_api(credit_card_access_manager()).OnVirtualCardUnmaskCancelled();
EXPECT_FALSE(otp_authenticator_->on_challenge_option_selected_invoked());
}
// Test that a success response for a VCN 3DS authentication is handled
// correctly and notifies the caller with the proper fields set, and both the
// authentication unmasked server card result and prompt completed better auth
// flow event metric buckets are logged to.
TEST_F(CreditCardAccessManagerTest,
VirtualCardUnmasking_3dsResponseReceived_Success) {
// Set up the test.
base::HistogramTester histogram_tester;
CreditCard card = test::GetVirtualCard();
FetchCreditCard(&card);
test_api(credit_card_access_manager())
.set_unmask_auth_flow_type(UnmaskAuthFlowType::kThreeDomainSecure);
payments::PaymentsWindowManager::Vcn3dsAuthenticationResponse response;
response.card = card;
response.result =
payments::PaymentsWindowManager::Vcn3dsAuthenticationResult::kSuccess;
// Mock the VCN 3DS authentication response.
test_api(credit_card_access_manager())
.OnVcn3dsAuthenticationComplete(response);
// Check that `accessor_` was triggered with the expected values.
EXPECT_EQ(accessor_->number(), response.card->number());
EXPECT_EQ(accessor_->cvc(), response.card->cvc());
EXPECT_FALSE(
test_api(credit_card_access_manager()).is_authentication_in_progress());
histogram_tester.ExpectUniqueSample(
"Autofill.ServerCardUnmask.VirtualCard.Result.ThreeDomainSecure",
autofill_metrics::ServerCardUnmaskResult::kAuthenticationUnmasked, 1);
histogram_tester.ExpectUniqueSample(
"Autofill.BetterAuth.FlowEvents.ThreeDomainSecure",
CreditCardFormEventLogger::UnmaskAuthFlowEvent::kPromptCompleted, 1);
}
// Test that a failure response for a VCN 3DS authentication is handled
// correctly and notifies the caller with the proper fields set, and the virtual
// card retrieval error server card unmask result metric bucket is logged to.
TEST_F(CreditCardAccessManagerTest,
VirtualCardUnmasking_3dsResponseReceived_AuthenticationError) {
// Set up the test.
base::HistogramTester histogram_tester;
CreditCard card = test::GetVirtualCard();
FetchCreditCard(&card);
payments::PaymentsWindowManager::Vcn3dsAuthenticationResponse response;
response.result = payments::PaymentsWindowManager::
Vcn3dsAuthenticationResult::kAuthenticationFailed;
// Mock the VCN 3DS authentication response.
test_api(credit_card_access_manager())
.OnVcn3dsAuthenticationComplete(response);
EXPECT_FALSE(
test_api(credit_card_access_manager()).is_authentication_in_progress());
histogram_tester.ExpectUniqueSample(
"Autofill.ServerCardUnmask.VirtualCard.Result.ThreeDomainSecure",
autofill_metrics::ServerCardUnmaskResult::kVirtualCardRetrievalError, 1);
}
// Test that the user cancelling a VCN 3DS authentication is handled correctly
// and notifies the caller with the proper fields set, and the flow cancelled
// result metric bucket is logged to.
TEST_F(
CreditCardAccessManagerTest,
VirtualCardUnmasking_3dsResponseReceived_AuthenticationNotCompletedError) {
// Set up the test.
base::HistogramTester histogram_tester;
CreditCard card = test::GetVirtualCard();
FetchCreditCard(&card);
payments::PaymentsWindowManager::Vcn3dsAuthenticationResponse response;
response.result = payments::PaymentsWindowManager::
Vcn3dsAuthenticationResult::kAuthenticationNotCompleted;
// Mock the VCN 3DS authentication response.
test_api(credit_card_access_manager())
.OnVcn3dsAuthenticationComplete(response);
histogram_tester.ExpectUniqueSample(
"Autofill.ServerCardUnmask.VirtualCard.Result.ThreeDomainSecure",
autofill_metrics::ServerCardUnmaskResult::kFlowCancelled, 1);
}
TEST_F(CreditCardAccessManagerTest, Prefetching_RiskData) {
base::test::ScopedFeatureList scoped_feature_list{
features::kAutofillEnablePrefetchingRiskDataForRetrieval};
// Setting up a server card.
CreateServerCard(kTestGUID, kTestNumber);
credit_card_access_manager().PrepareToFetchCreditCard();
EXPECT_TRUE(autofill_client_.GetPaymentsAutofillClient()->risk_data_loaded());
}
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_ANDROID)
// Ensures that the virtual card risk-based unmasking response is handled
// correctly and authentication is delegated to the FIDO authenticator, when
// only the FIDO challenge options is returned.
TEST_F(CreditCardAccessManagerTest,
RiskBasedVirtualCardUnmasking_AuthenticationRequired_FidoOnly) {
base::HistogramTester histogram_tester;
const CreditCard* virtual_card =
CreateServerCard(kTestGUID, kTestNumber, kTestServerId, kTestCvc16,
CreditCard::RecordType::kVirtualCard);
// TODO(crbug.com/40197696): Switch to SetUserVerifiable after moving all
// is_user_veriable_ related logic from CreditCardAccessManager to
// CreditCardFidoAuthenticator.
test_api(credit_card_access_manager()).set_is_user_verifiable(true);
fido_authenticator().set_is_user_opted_in(true);
FetchCreditCard(virtual_card);
// This checks risk-based authentication flow is successfully invoked,
// because it is always the very first authentication flow in a VCN
// unmasking flow.
EXPECT_TRUE(autofill_client_.GetPaymentsAutofillClient()
->risk_based_authentication_invoked());
credit_card_access_manager().OnRiskBasedAuthenticationResponseReceived(
CreditCardRiskBasedAuthenticator::RiskBasedAuthenticationResponse()
.with_result(CreditCardRiskBasedAuthenticator::
RiskBasedAuthenticationResponse::Result::
kAuthenticationRequired)
.with_fido_request_options(GetTestRequestOptions())
.with_context_token("fake_context_token"));
// Expect the CreditCardAccessManager invokes the FIDO authenticator.
ASSERT_TRUE(fido_authenticator().authenticate_invoked());
EXPECT_EQ(fido_authenticator().card().number(),
base::UTF8ToUTF16(std::string(kTestNumber)));
EXPECT_EQ(fido_authenticator().card().record_type(),
CreditCard::RecordType::kVirtualCard);
ASSERT_TRUE(fido_authenticator().context_token().has_value());
EXPECT_EQ(fido_authenticator().context_token().value(), "fake_context_token");
// Mock FIDO authentication completed.
CreditCardFidoAuthenticator::FidoAuthenticationResponse fido_response;
fido_response.did_succeed = true;
CreditCard card = test::WithCvc(test::GetVirtualCard(), u"234");
fido_response.card = &card;
test_api(credit_card_access_manager())
.OnFIDOAuthenticationComplete(fido_response);
// Expect accessor to successfully retrieve the virtual card CVC.
EXPECT_EQ(u"234", accessor_->cvc());
// Expect the metrics are logged correctly.
histogram_tester.ExpectUniqueSample(
"Autofill.ServerCardUnmask.VirtualCard.Attempt", true, 1);
histogram_tester.ExpectUniqueSample(
"Autofill.ServerCardUnmask.VirtualCard.Result.Fido",
autofill_metrics::ServerCardUnmaskResult::kAuthenticationUnmasked, 1);
}
// Ensures that the virtual card risk-based unmasking response is handled
// correctly and authentication is delegated to the FIDO authenticator, when
// both FIDO and OTP challenge options are returned.
TEST_F(
CreditCardAccessManagerTest,
RiskBasedVirtualCardUnmasking_AuthenticationRequired_FidoAndOtp_PrefersFido) {
base::HistogramTester histogram_tester;
const CreditCard* virtual_card =
CreateServerCard(kTestGUID, kTestNumber, kTestServerId, kTestCvc16,
CreditCard::RecordType::kVirtualCard);
// TODO(crbug.com/40197696): Switch to SetUserVerifiable after moving all
// is_user_veriable_ related logic from CreditCardAccessManager to
// CreditCardFidoAuthenticator.
test_api(credit_card_access_manager()).set_is_user_verifiable(true);
fido_authenticator().set_is_user_opted_in(true);
FetchCreditCard(virtual_card);
// This checks risk-based authentication flow is successfully invoked,
// because it is always the very first authentication flow in a VCN
// unmasking flow.
EXPECT_TRUE(autofill_client_.GetPaymentsAutofillClient()
->risk_based_authentication_invoked());
// Mock server response with information regarding both FIDO and OTP auth.
CreditCardRiskBasedAuthenticator::RiskBasedAuthenticationResponse response;
response.context_token = "fake_context_token";
CardUnmaskChallengeOption challenge_option =
test::GetCardUnmaskChallengeOptions(
{CardUnmaskChallengeOptionType::kSmsOtp})[0];
response.card_unmask_challenge_options.emplace_back(challenge_option);
response.fido_request_options = GetTestRequestOptions();
response.result = CreditCardRiskBasedAuthenticator::
RiskBasedAuthenticationResponse::Result::kAuthenticationRequired;
credit_card_access_manager().OnRiskBasedAuthenticationResponseReceived(
response);
// Expect the CreditCardAccessManager invokes the FIDO authenticator.
ASSERT_TRUE(fido_authenticator().authenticate_invoked());
EXPECT_EQ(fido_authenticator().card().number(),
base::UTF8ToUTF16(std::string(kTestNumber)));
EXPECT_EQ(fido_authenticator().card().record_type(),
CreditCard::RecordType::kVirtualCard);
ASSERT_TRUE(fido_authenticator().context_token().has_value());
EXPECT_EQ(fido_authenticator().context_token().value(), "fake_context_token");
// Mock FIDO authentication completed.
CreditCardFidoAuthenticator::FidoAuthenticationResponse fido_response;
fido_response.did_succeed = true;
CreditCard card = test::GetVirtualCard();
fido_response.card = &card;
fido_response.cvc = u"123";
test_api(credit_card_access_manager())
.OnFIDOAuthenticationComplete(fido_response);
// Expect the metrics are logged correctly.
histogram_tester.ExpectUniqueSample(
"Autofill.ServerCardUnmask.VirtualCard.Attempt", true, 1);
histogram_tester.ExpectUniqueSample(
"Autofill.ServerCardUnmask.VirtualCard.Result.Fido",
autofill_metrics::ServerCardUnmaskResult::kAuthenticationUnmasked, 1);
}
// Ensures that the virtual card risk-based unmasking response is handled
// correctly and authentication is delegated to the OTP authenticator, when both
// FIDO and OTP challenge options are returned but FIDO local preference is not
// opted in.
TEST_F(
CreditCardAccessManagerTest,
RiskBasedVirtualCardUnmasking_AuthenticationRequired_FidoAndOtp_FidoNotOptedIn) {
base::HistogramTester histogram_tester;
std::vector<CardUnmaskChallengeOption> challenge_options =
test::GetCardUnmaskChallengeOptions(
{CardUnmaskChallengeOptionType::kSmsOtp});
MockCardUnmaskFlowUpToAuthenticationSelectionDialogAccepted(
/*fido_authenticator_is_user_opted_in=*/false,
/*is_user_verifiable=*/true, challenge_options,
/*selected_index=*/0, CreditCard::RecordType::kVirtualCard);
CreditCardOtpAuthenticator::OtpAuthenticationResponse otp_response;
otp_response.result =
CreditCardOtpAuthenticator::OtpAuthenticationResponse::Result::kSuccess;
CreditCard card = test::GetVirtualCard();
otp_response.card = &card;
otp_response.cvc = u"123";
credit_card_access_manager().OnOtpAuthenticationComplete(otp_response);
// Expect the metrics are logged correctly.
histogram_tester.ExpectUniqueSample(
"Autofill.ServerCardUnmask.VirtualCard.Attempt", true, 1);
histogram_tester.ExpectUniqueSample(
"Autofill.ServerCardUnmask.VirtualCard.Result.Otp",
autofill_metrics::ServerCardUnmaskResult::kAuthenticationUnmasked, 1);
}
// Ensures that the virtual card risk-based unmasking response is handled
// correctly and authentication is delegated first to the FIDO authenticator,
// when both FIDO and OTP challenge options are returned, but fall back to OTP
// authentication if FIDO failed.
TEST_F(
CreditCardAccessManagerTest,
RiskBasedVirtualCardUnmasking_AuthenticationRequired_FidoAndOtp_FidoFailedFallBackToOtp) {
base::HistogramTester histogram_tester;
std::vector<CardUnmaskChallengeOption> challenge_options =
test::GetCardUnmaskChallengeOptions(
{CardUnmaskChallengeOptionType::kSmsOtp});
MockCardUnmaskFlowUpToAuthenticationSelectionDialogAccepted(
/*fido_authenticator_is_user_opted_in=*/true,
/*is_user_verifiable=*/true, challenge_options,
/*selected_index=*/0, CreditCard::RecordType::kVirtualCard);
CreditCardOtpAuthenticator::OtpAuthenticationResponse otp_response;
otp_response.result =
CreditCardOtpAuthenticator::OtpAuthenticationResponse::Result::kSuccess;
CreditCard card = test::GetVirtualCard();
otp_response.card = &card;
otp_response.cvc = u"123";
credit_card_access_manager().OnOtpAuthenticationComplete(otp_response);
// Expect the metrics are logged correctly.
histogram_tester.ExpectUniqueSample(
"Autofill.ServerCardUnmask.VirtualCard.Attempt", true, 1);
histogram_tester.ExpectUniqueSample(
"Autofill.ServerCardUnmask.VirtualCard.Result.OtpFallbackFromFido",
autofill_metrics::ServerCardUnmaskResult::kAuthenticationUnmasked, 1);
}
// Ensures that the virtual card risk-based unmasking response is handled
// correctly if there is only FIDO option returned by the server but the user
// is not opted in.
TEST_F(
CreditCardAccessManagerTest,
RiskBasedVirtualCardUnmasking_AuthenticationRequired_FidoOnly_FidoNotOptedIn) {
base::HistogramTester histogram_tester;
const CreditCard* masked_server_card =
CreateServerCard(kTestGUID, kTestNumber, kTestServerId);
CreditCard virtual_card = *masked_server_card;
virtual_card.set_record_type(CreditCard::RecordType::kVirtualCard);
// TODO(crbug.com/40197696): Switch to SetUserVerifiable after moving all
// is_user_veriable_ related logic from CreditCardAccessManager to
// CreditCardFidoAuthenticator.
test_api(credit_card_access_manager()).set_is_user_verifiable(true);
fido_authenticator().set_is_user_opted_in(false);
FetchCreditCard(&virtual_card);
// This checks risk-based authentication flow is successfully invoked,
// because it is always the very first authentication flow in a VCN
// unmasking flow.
EXPECT_TRUE(autofill_client_.GetPaymentsAutofillClient()
->risk_based_authentication_invoked());
credit_card_access_manager().OnRiskBasedAuthenticationResponseReceived(
CreditCardRiskBasedAuthenticator::RiskBasedAuthenticationResponse()
.with_result(CreditCardRiskBasedAuthenticator::
RiskBasedAuthenticationResponse::Result::
kAuthenticationRequired)
.with_fido_request_options(GetTestRequestOptions())
.with_context_token("fake_context_token"));
// Expect the CreditCardAccessManager to end the session.
EXPECT_FALSE(otp_authenticator_->on_challenge_option_selected_invoked());
EXPECT_FALSE(fido_authenticator().authenticate_invoked());
// Expect the metrics are logged correctly.
histogram_tester.ExpectUniqueSample(
"Autofill.ServerCardUnmask.VirtualCard.Attempt", true, 1);
histogram_tester.ExpectUniqueSample(
"Autofill.ServerCardUnmask.VirtualCard.Result.Fido",
autofill_metrics::ServerCardUnmaskResult::kOnlyFidoAvailableButNotOptedIn,
1);
}
#endif // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_ANDROID)
#endif // !BUILDFLAG(IS_IOS)
// Ensures that the virtual card risk-based unmasking response is handled
// correctly if there is no challenge option returned by the server.
TEST_F(CreditCardAccessManagerTest,
RiskBasedVirtualCardUnmasking_Failure_NoOptionReturned) {
base::HistogramTester histogram_tester;
const CreditCard* masked_server_card =
CreateServerCard(kTestGUID, kTestNumber, kTestServerId);
CreditCard virtual_card = *masked_server_card;
virtual_card.set_record_type(CreditCard::RecordType::kVirtualCard);
// TODO(crbug.com/40197696): Switch to SetUserVerifiable after moving all
// |is_user_verifiable_| related logic from CreditCardAccessManager to
// CreditCardFidoAuthenticator.
test_api(credit_card_access_manager()).set_is_user_verifiable(true);
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_ANDROID)
fido_authenticator().set_is_user_opted_in(true);
#endif
FetchCreditCard(&virtual_card);
// This checks risk-based authentication flow is successfully invoked,
// because it is always the very first authentication flow in a VCN
// unmasking flow.
EXPECT_TRUE(autofill_client_.GetPaymentsAutofillClient()
->risk_based_authentication_invoked());
credit_card_access_manager().OnRiskBasedAuthenticationResponseReceived(
CreditCardRiskBasedAuthenticator::RiskBasedAuthenticationResponse()
.with_result(CreditCardRiskBasedAuthenticator::
RiskBasedAuthenticationResponse::Result::kError)
.with_context_token("fake_context_token"));
// Expect the CreditCardAccessManager to end the session.
EXPECT_FALSE(otp_authenticator_->on_challenge_option_selected_invoked());
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_ANDROID)
EXPECT_FALSE(fido_authenticator().authenticate_invoked());
#endif
// Expect the metrics are logged correctly.
histogram_tester.ExpectUniqueSample(
"Autofill.ServerCardUnmask.VirtualCard.Attempt", true, 1);
histogram_tester.ExpectUniqueSample(
"Autofill.ServerCardUnmask.VirtualCard.Result.RiskBased",
autofill_metrics::ServerCardUnmaskResult::kAuthenticationError, 1);
}
// Ensures that the virtual card risk-based unmasking response is handled
// correctly if there is virtual card retrieval error.
TEST_F(CreditCardAccessManagerTest,
RiskBasedVirtualCardUnmasking_Failure_VirtualCardRetrievalError) {
base::HistogramTester histogram_tester;
const CreditCard* masked_server_card =
CreateServerCard(kTestGUID, kTestNumber, kTestServerId);
CreditCard virtual_card = *masked_server_card;
virtual_card.set_record_type(CreditCard::RecordType::kVirtualCard);
// TODO(crbug.com/40197696): Switch to SetUserVerifiable after moving all
// is_user_veriable_ related logic from CreditCardAccessManager to
// CreditCardFidoAuthenticator.
test_api(credit_card_access_manager()).set_is_user_verifiable(true);
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_ANDROID)
fido_authenticator().set_is_user_opted_in(true);
#endif
FetchCreditCard(&virtual_card);
// This checks risk-based authentication flow is successfully invoked,
// because it is always the very first authentication flow in a VCN
// unmasking flow.
EXPECT_TRUE(autofill_client_.GetPaymentsAutofillClient()
->risk_based_authentication_invoked());
credit_card_access_manager().OnRiskBasedAuthenticationResponseReceived(
CreditCardRiskBasedAuthenticator::RiskBasedAuthenticationResponse()
.with_result(CreditCardRiskBasedAuthenticator::
RiskBasedAuthenticationResponse::Result::
kVirtualCardRetrievalError));
// Expect the CreditCardAccessManager to end the session.
EXPECT_FALSE(otp_authenticator_->on_challenge_option_selected_invoked());
EXPECT_TRUE(autofill_client_.GetPaymentsAutofillClient()
->autofill_error_dialog_shown());
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_ANDROID)
EXPECT_FALSE(fido_authenticator().authenticate_invoked());
#endif
// Expect the metrics are logged correctly.
histogram_tester.ExpectUniqueSample(
"Autofill.ServerCardUnmask.VirtualCard.Attempt", true, 1);
histogram_tester.ExpectUniqueSample(
"Autofill.ServerCardUnmask.VirtualCard.Result.RiskBased",
autofill_metrics::ServerCardUnmaskResult::kVirtualCardRetrievalError, 1);
}
TEST_F(CreditCardAccessManagerTest,
RiskBasedVirtualCardUnmasking_Failure_MerchantOptedOut) {
base::HistogramTester histogram_tester;
const CreditCard* masked_server_card =
CreateServerCard(kTestGUID, kTestNumber, kTestServerId);
CreditCard virtual_card = *masked_server_card;
virtual_card.set_record_type(CreditCard::RecordType::kVirtualCard);
FetchCreditCard(&virtual_card);
AutofillErrorDialogContext autofill_error_dialog_context;
autofill_error_dialog_context.server_returned_title =
"test_server_returned_title";
autofill_error_dialog_context.server_returned_description =
"test_server_returned_description";
CreditCardRiskBasedAuthenticator::RiskBasedAuthenticationResponse response;
response.error_dialog_context = autofill_error_dialog_context;
response.result = CreditCardRiskBasedAuthenticator::
RiskBasedAuthenticationResponse::Result::kVirtualCardRetrievalError;
credit_card_access_manager().OnRiskBasedAuthenticationResponseReceived(
response);
EXPECT_TRUE(autofill_client_.GetPaymentsAutofillClient()
->autofill_error_dialog_shown());
const AutofillErrorDialogContext& displayed_error_dialog_context =
autofill_client_.GetPaymentsAutofillClient()
->autofill_error_dialog_context();
EXPECT_EQ(*displayed_error_dialog_context.server_returned_title,
*autofill_error_dialog_context.server_returned_title);
EXPECT_EQ(*displayed_error_dialog_context.server_returned_description,
*autofill_error_dialog_context.server_returned_description);
histogram_tester.ExpectUniqueSample(
"Autofill.ServerCardUnmask.VirtualCard.Result.RiskBased",
autofill_metrics::ServerCardUnmaskResult::kVirtualCardRetrievalError, 1);
}
TEST_F(CreditCardAccessManagerTest,
RiskBasedVirtualCardUnmasking_FlowCancelled) {
base::HistogramTester histogram_tester;
const CreditCard* masked_server_card =
CreateServerCard(kTestGUID, kTestNumber, kTestServerId);
CreditCard virtual_card = *masked_server_card;
virtual_card.set_record_type(CreditCard::RecordType::kVirtualCard);
// TODO(crbug.com/40197696): Switch to SetUserVerifiable after moving all
// is_user_veriable_ related logic from CreditCardAccessManager to
// CreditCardFidoAuthenticator.
test_api(credit_card_access_manager()).set_is_user_verifiable(true);
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_ANDROID)
fido_authenticator().set_is_user_opted_in(true);
#endif
FetchCreditCard(&virtual_card);
// This checks risk-based authentication flow is successfully invoked,
// because it is always the very first authentication flow in a VCN
// unmasking flow.
EXPECT_TRUE(autofill_client_.GetPaymentsAutofillClient()
->risk_based_authentication_invoked());
// Mock that the flow was cancelled by the user.
test_api(credit_card_access_manager()).OnVirtualCardUnmaskCancelled();
EXPECT_FALSE(otp_authenticator_->on_challenge_option_selected_invoked());
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_ANDROID)
EXPECT_FALSE(fido_authenticator().authenticate_invoked());
#endif
// Expect the metrics are logged correctly.
histogram_tester.ExpectUniqueSample(
"Autofill.ServerCardUnmask.VirtualCard.Attempt", true, 1);
histogram_tester.ExpectUniqueSample(
"Autofill.ServerCardUnmask.VirtualCard.Result.RiskBased",
autofill_metrics::ServerCardUnmaskResult::kFlowCancelled, 1);
}
// Test that the CreditCardAccessManager's destructor resets the record type of
// the card that had no interactive authentication flows completed in the
// associated FormDataImporter.
TEST_F(CreditCardAccessManagerTest, DestructorResetsCardIdentifier) {
auto* form_data_importer = autofill_client_.GetFormDataImporter();
form_data_importer
->SetPaymentMethodTypeIfNonInteractiveAuthenticationFlowCompleted(
NonInteractivePaymentMethodType::kLocalCard);
EXPECT_TRUE(
test_api(*form_data_importer)
.payment_method_type_if_non_interactive_authentication_flow_completed()
.has_value());
autofill_driver_.reset();
EXPECT_FALSE(
test_api(*form_data_importer)
.payment_method_type_if_non_interactive_authentication_flow_completed()
.has_value());
}
TEST_F(CreditCardAccessManagerTest, InvokeVirtualCardEnrollmentPreflightCall) {
base::test::ScopedFeatureList feature_list;
feature_list.InitAndEnableFeature(
features::
kAutofillEnableMultipleRequestInVirtualCardDownstreamEnrollment);
auto virtual_card_enrollment_manager =
std::make_unique<MockVirtualCardEnrollmentManager>(
&personal_data().payments_data_manager(),
/*payments_network_interface=*/nullptr, &autofill_client_);
autofill_client_.GetPaymentsAutofillClient()
->set_virtual_card_enrollment_manager(
std::move(virtual_card_enrollment_manager));
CreditCard card = test::GetMaskedServerCard();
card.set_virtual_card_enrollment_state(
CreditCard::VirtualCardEnrollmentState::kUnenrolledAndEligible);
personal_data().test_payments_data_manager().AddServerCreditCard(card);
EXPECT_CALL(*static_cast<MockVirtualCardEnrollmentManager*>(
autofill_client_.GetPaymentsAutofillClient()
->GetVirtualCardEnrollmentManager()),
InitVirtualCardEnroll);
PrepareToFetchCreditCardAndWaitForCallbacks();
CreditCardAccessManagerTestBase::FetchCreditCard(&card);
}
TEST_F(CreditCardAccessManagerTest,
DoNotInvokeVirtualCardEnrollmentPreflightCall_FlagOff) {
base::test::ScopedFeatureList feature_list;
feature_list.InitAndDisableFeature(
features::
kAutofillEnableMultipleRequestInVirtualCardDownstreamEnrollment);
auto virtual_card_enrollment_manager =
std::make_unique<MockVirtualCardEnrollmentManager>(
&personal_data().payments_data_manager(),
/*payments_network_interface=*/nullptr, &autofill_client_);
autofill_client_.GetPaymentsAutofillClient()
->set_virtual_card_enrollment_manager(
std::move(virtual_card_enrollment_manager));
CreditCard card = test::GetMaskedServerCard();
card.set_virtual_card_enrollment_state(
CreditCard::VirtualCardEnrollmentState::kUnenrolledAndEligible);
personal_data().test_payments_data_manager().AddServerCreditCard(card);
EXPECT_CALL(*static_cast<MockVirtualCardEnrollmentManager*>(
autofill_client_.GetPaymentsAutofillClient()
->GetVirtualCardEnrollmentManager()),
InitVirtualCardEnroll)
.Times(0);
PrepareToFetchCreditCardAndWaitForCallbacks();
CreditCardAccessManagerTestBase::FetchCreditCard(&card);
}
TEST_F(CreditCardAccessManagerTest,
DoNotInvokeVirtualCardEnrollmentPreflightCall_CardNotEligible) {
base::test::ScopedFeatureList feature_list;
feature_list.InitAndEnableFeature(
features::
kAutofillEnableMultipleRequestInVirtualCardDownstreamEnrollment);
auto virtual_card_enrollment_manager =
std::make_unique<MockVirtualCardEnrollmentManager>(
&personal_data().payments_data_manager(),
/*payments_network_interface=*/nullptr, &autofill_client_);
autofill_client_.GetPaymentsAutofillClient()
->set_virtual_card_enrollment_manager(
std::move(virtual_card_enrollment_manager));
CreditCard card = test::GetMaskedServerCard();
card.set_virtual_card_enrollment_state(
CreditCard::VirtualCardEnrollmentState::kUnenrolledAndNotEligible);
personal_data().test_payments_data_manager().AddServerCreditCard(card);
EXPECT_CALL(*static_cast<MockVirtualCardEnrollmentManager*>(
autofill_client_.GetPaymentsAutofillClient()
->GetVirtualCardEnrollmentManager()),
InitVirtualCardEnroll)
.Times(0);
PrepareToFetchCreditCardAndWaitForCallbacks();
CreditCardAccessManagerTestBase::FetchCreditCard(&card);
}
} // namespace autofill