blob: 3903e7af2cf0b4652e5862f4393bc89a7416cb36 [file] [log] [blame]
// Copyright 2019 The Chromium Authors. All rights reserved.
// 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 <algorithm>
#include <memory>
#include <string>
#include <tuple>
#include <utility>
#include <vector>
#include "base/base64.h"
#include "base/command_line.h"
#include "base/feature_list.h"
#include "base/memory/ref_counted.h"
#include "base/metrics/field_trial.h"
#include "base/metrics/metrics_hashes.h"
#include "base/run_loop.h"
#include "base/stl_util.h"
#include "base/strings/string16.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/post_task.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "components/autofill/core/browser/autocomplete_history_manager.h"
#include "components/autofill/core/browser/autofill_download_manager.h"
#include "components/autofill/core/browser/autofill_test_utils.h"
#include "components/autofill/core/browser/data_model/autofill_profile.h"
#include "components/autofill/core/browser/data_model/credit_card.h"
#include "components/autofill/core/browser/form_structure.h"
#include "components/autofill/core/browser/metrics/form_events.h"
#include "components/autofill/core/browser/payments/test_payments_client.h"
#include "components/autofill/core/browser/personal_data_manager.h"
#include "components/autofill/core/browser/test_autofill_client.h"
#include "components/autofill/core/browser/test_autofill_clock.h"
#include "components/autofill/core/browser/test_autofill_driver.h"
#include "components/autofill/core/browser/test_personal_data_manager.h"
#include "components/autofill/core/browser/validation.h"
#include "components/autofill/core/browser/webdata/autofill_webdata_service.h"
#include "components/autofill/core/common/autofill_clock.h"
#include "components/autofill/core/common/autofill_features.h"
#include "components/autofill/core/common/autofill_payments_features.h"
#include "components/autofill/core/common/autofill_prefs.h"
#include "components/autofill/core/common/autofill_switches.h"
#include "components/autofill/core/common/autofill_util.h"
#include "components/autofill/core/common/form_data.h"
#include "components/prefs/pref_service.h"
#include "components/security_state/core/security_state.h"
#include "components/strings/grit/components_strings.h"
#include "components/sync/driver/test_sync_service.h"
#include "components/version_info/channel.h"
#include "net/base/url_util.h"
#include "services/metrics/public/cpp/ukm_builders.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/gfx/geometry/rect.h"
#include "url/gurl.h"
#if !defined(OS_IOS)
#include "components/autofill/core/browser/payments/fido_authentication_strike_database.h"
#include "components/autofill/core/browser/payments/test_credit_card_fido_authenticator.h"
#endif
using base::ASCIIToUTF16;
namespace autofill {
namespace {
const char kTestGUID[] = "00000000-0000-0000-0000-000000000001";
const char kTestNumber[] = "4234567890123456"; // Visa
#if !defined(OS_IOS)
// Base64 encoding of "This is a test challenge".
constexpr char kTestChallenge[] = "VGhpcyBpcyBhIHRlc3QgY2hhbGxlbmdl";
// Base64 encoding of "This is a test Credential ID".
const char kCredentialId[] = "VGhpcyBpcyBhIHRlc3QgQ3JlZGVudGlhbCBJRC4=";
const char kGooglePaymentsRpid[] = "google.com";
std::string BytesToBase64(const std::vector<uint8_t> bytes) {
std::string base64;
base::Base64Encode(std::string(bytes.begin(), bytes.end()), &base64);
return base64;
}
#endif
class TestAccessor : public CreditCardAccessManager::Accessor {
public:
TestAccessor() {}
base::WeakPtr<TestAccessor> GetWeakPtr() {
return weak_ptr_factory_.GetWeakPtr();
}
void OnCreditCardFetched(bool did_succeed,
const CreditCard* card,
const base::string16& cvc) override {
did_succeed_ = did_succeed;
if (did_succeed_) {
DCHECK(card);
number_ = card->number();
}
}
base::string16 number() { return number_; }
bool did_succeed() { return did_succeed_; }
private:
// Is set to true if authentication was successful.
bool did_succeed_ = false;
// The card number returned from OnCreditCardFetched().
base::string16 number_;
base::WeakPtrFactory<TestAccessor> weak_ptr_factory_{this};
};
std::string NextYear() {
base::Time::Exploded now;
base::Time::Now().LocalExplode(&now);
return base::NumberToString(now.year + 1);
}
std::string NextMonth() {
base::Time::Exploded now;
base::Time::Now().LocalExplode(&now);
return base::NumberToString(now.month % 12 + 1);
}
} // namespace
class CreditCardAccessManagerTest : public testing::Test {
public:
CreditCardAccessManagerTest()
: task_environment_(
base::test::TaskEnvironment::MainThreadType::DEFAULT,
base::test::TaskEnvironment::ThreadPoolExecutionMode::QUEUED) {}
void SetUp() override {
autofill_client_.SetPrefs(test::PrefServiceForTesting());
personal_data_manager_.Init(/*profile_database=*/database_,
/*account_database=*/nullptr,
/*pref_service=*/autofill_client_.GetPrefs(),
/*identity_manager=*/nullptr,
/*client_profile_validator=*/nullptr,
/*history_service=*/nullptr,
/*is_off_the_record=*/false);
personal_data_manager_.SetPrefService(autofill_client_.GetPrefs());
accessor_.reset(new TestAccessor());
autofill_driver_ =
std::make_unique<testing::NiceMock<TestAutofillDriver>>();
payments_client_ = new payments::TestPaymentsClient(
autofill_driver_->GetURLLoaderFactory(),
autofill_client_.GetIdentityManager(), &personal_data_manager_);
autofill_client_.set_test_payments_client(
std::unique_ptr<payments::TestPaymentsClient>(payments_client_));
autofill_client_.set_test_strike_database(
std::make_unique<TestStrikeDatabase>());
credit_card_access_manager_ = std::make_unique<CreditCardAccessManager>(
autofill_driver_.get(), &autofill_client_, &personal_data_manager_,
nullptr);
#if !defined(OS_IOS)
credit_card_access_manager_->set_fido_authenticator_for_testing(
std::make_unique<TestCreditCardFIDOAuthenticator>(
autofill_driver_.get(), &autofill_client_));
#endif
}
void TearDown() override {
// Order of destruction is important as AutofillDriver relies on
// PersonalDataManager to be around when it gets destroyed.
autofill_driver_.reset();
personal_data_manager_.SetPrefService(nullptr);
personal_data_manager_.ClearCreditCards();
}
bool IsAuthenticationInProgress() {
return credit_card_access_manager_->is_authentication_in_progress();
}
void ResetPreflightCallLimiter() {
credit_card_access_manager_->can_fetch_unmask_details_.Signal();
credit_card_access_manager_->is_user_verifiable_ = base::nullopt;
}
void CreateLocalCard(std::string guid, std::string number = std::string()) {
CreditCard local_card = CreditCard();
test::SetCreditCardInfo(&local_card, "Elvis Presley", number.c_str(),
NextMonth().c_str(), NextYear().c_str(), "1");
local_card.set_guid(guid);
local_card.set_record_type(CreditCard::LOCAL_CARD);
personal_data_manager_.ClearCreditCards();
personal_data_manager_.AddCreditCard(local_card);
}
void CreateServerCard(std::string guid, std::string number = std::string()) {
CreditCard masked_server_card = CreditCard();
test::SetCreditCardInfo(&masked_server_card, "Elvis Presley",
number.c_str(), NextMonth().c_str(),
NextYear().c_str(), "1");
masked_server_card.set_guid(guid);
masked_server_card.set_record_type(CreditCard::MASKED_SERVER_CARD);
personal_data_manager_.ClearCreditCards();
personal_data_manager_.AddServerCreditCard(masked_server_card);
}
CreditCardCVCAuthenticator* GetCVCAuthenticator() {
return credit_card_access_manager_->GetOrCreateCVCAuthenticator();
}
// Returns true if full card request was sent from CVC auth.
bool GetRealPanForCVCAuth(AutofillClient::PaymentsRpcResult result,
const std::string& real_pan,
bool fido_opt_in = false,
bool follow_with_fido_auth = false) {
payments::FullCardRequest* full_card_request =
GetCVCAuthenticator()->full_card_request_.get();
if (!full_card_request)
return false;
payments::PaymentsClient::UnmaskResponseDetails response;
#if !defined(OS_IOS)
response.card_authorization_token = "dummy_card_authorization_token";
if (fido_opt_in) {
response.fido_creation_options = GetTestCreationOptions();
}
if (follow_with_fido_auth) {
response.fido_request_options = GetTestRequestOptions();
}
#endif
full_card_request->OnDidGetRealPan(result,
response.with_real_pan(real_pan));
return true;
}
#if !defined(OS_IOS)
void ClearStrikes() {
return GetFIDOAuthenticator()
->GetOrCreateFidoAuthenticationStrikeDatabase()
->ClearAllStrikes();
}
int GetStrikes() {
return GetFIDOAuthenticator()
->GetOrCreateFidoAuthenticationStrikeDatabase()
->GetStrikes();
}
base::Value GetTestRequestOptions() {
base::Value request_options = base::Value(base::Value::Type::DICTIONARY);
request_options.SetKey("challenge", base::Value(kTestChallenge));
request_options.SetKey("relying_party_id",
base::Value(kGooglePaymentsRpid));
base::Value key_info(base::Value::Type::DICTIONARY);
key_info.SetKey("credential_id", base::Value(kCredentialId));
request_options.SetKey("key_info", base::Value(base::Value::Type::LIST));
request_options.FindKeyOfType("key_info", base::Value::Type::LIST)
->GetList()
.push_back(std::move(key_info));
return request_options;
}
base::Value GetTestCreationOptions() {
base::Value creation_options = base::Value(base::Value::Type::DICTIONARY);
creation_options.SetKey("challenge", base::Value(kTestChallenge));
creation_options.SetKey("relying_party_id",
base::Value(kGooglePaymentsRpid));
return creation_options;
}
void SetUserOptedIn(bool user_is_opted_in) {
scoped_feature_list_.InitAndEnableFeature(
features::kAutofillCreditCardAuthentication);
::autofill::prefs::SetCreditCardFIDOAuthEnabled(autofill_client_.GetPrefs(),
user_is_opted_in);
}
// Returns true if full card request was sent from FIDO auth.
bool GetRealPanForFIDOAuth(AutofillClient::PaymentsRpcResult result,
const std::string& real_pan) {
payments::FullCardRequest* full_card_request =
GetFIDOAuthenticator()->full_card_request_.get();
if (!full_card_request)
return false;
payments::PaymentsClient::UnmaskResponseDetails response;
full_card_request->OnDidGetRealPan(result,
response.with_real_pan(real_pan));
return true;
}
// Mocks an OptChange response from Payments Client.
void OptChange(AutofillClient::PaymentsRpcResult result,
bool user_is_opted_in,
bool include_creation_options = false,
bool include_request_options = false) {
payments::PaymentsClient::OptChangeResponseDetails response;
response.user_is_opted_in = user_is_opted_in;
if (include_creation_options) {
response.fido_creation_options = GetTestCreationOptions();
}
if (include_request_options) {
response.fido_request_options = GetTestRequestOptions();
}
GetFIDOAuthenticator()->OnDidGetOptChangeResult(result, response);
}
// Mocks user response for the offer dialog.
void AcceptWebauthnOfferDialog(bool did_accept) {
GetFIDOAuthenticator()->OnWebauthnOfferDialogUserResponse(did_accept);
}
TestCreditCardFIDOAuthenticator* GetFIDOAuthenticator() {
return static_cast<TestCreditCardFIDOAuthenticator*>(
credit_card_access_manager_->GetOrCreateFIDOAuthenticator());
}
#endif
void InvokeUnmaskDetailsTimeout() {
credit_card_access_manager_->ready_to_start_authentication_.Signal();
credit_card_access_manager_->can_fetch_unmask_details_.Signal();
}
void WaitForCallbacks() { task_environment_.RunUntilIdle(); }
protected:
std::unique_ptr<TestAccessor> accessor_;
base::test::TaskEnvironment task_environment_;
payments::TestPaymentsClient* payments_client_;
TestAutofillClient autofill_client_;
std::unique_ptr<TestAutofillDriver> autofill_driver_;
scoped_refptr<AutofillWebDataService> database_;
TestPersonalDataManager personal_data_manager_;
base::test::ScopedFeatureList scoped_feature_list_;
std::unique_ptr<CreditCardAccessManager> credit_card_access_manager_;
};
// Ensures GetCreditCard() successfully retrieves Card.
TEST_F(CreditCardAccessManagerTest, GetCreditCardSuccess) {
CreateLocalCard(kTestGUID);
CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID);
EXPECT_NE(card, nullptr);
}
// Ensures GetCreditCard() returns nullptr for invalid GUID.
TEST_F(CreditCardAccessManagerTest, GetCreditCardFailure) {
CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID);
EXPECT_EQ(card, nullptr);
}
// Ensures DeleteCard() successfully removes local cards.
TEST_F(CreditCardAccessManagerTest, RemoveLocalCreditCard) {
CreateLocalCard(kTestGUID);
CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID);
EXPECT_TRUE(personal_data_manager_.GetCreditCardWithGUID(kTestGUID));
EXPECT_TRUE(credit_card_access_manager_->DeleteCard(card));
EXPECT_FALSE(personal_data_manager_.GetCreditCardWithGUID(kTestGUID));
}
// Ensures DeleteCard() does nothing for server cards.
TEST_F(CreditCardAccessManagerTest, RemoveServerCreditCard) {
CreateServerCard(kTestGUID);
CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID);
EXPECT_TRUE(personal_data_manager_.GetCreditCardWithGUID(kTestGUID));
EXPECT_FALSE(credit_card_access_manager_->DeleteCard(card));
// Cannot delete server cards.
EXPECT_TRUE(personal_data_manager_.GetCreditCardWithGUID(kTestGUID));
}
// Ensures GetDeletionConfirmationText(~) returns correct values for local
// cards.
TEST_F(CreditCardAccessManagerTest, LocalCardGetDeletionConfirmationText) {
CreateLocalCard(kTestGUID);
CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID);
base::string16 title = base::string16();
base::string16 body = base::string16();
EXPECT_TRUE(credit_card_access_manager_->GetDeletionConfirmationText(
card, &title, &body));
// |title| and |body| should be updated appropriately.
EXPECT_EQ(title, card->NetworkOrBankNameAndLastFourDigits());
EXPECT_EQ(body,
l10n_util::GetStringUTF16(
IDS_AUTOFILL_DELETE_CREDIT_CARD_SUGGESTION_CONFIRMATION_BODY));
}
// Ensures GetDeletionConfirmationText(~) returns false for server cards.
TEST_F(CreditCardAccessManagerTest, ServerCardGetDeletionConfirmationText) {
CreateServerCard(kTestGUID);
CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID);
base::string16 title = base::string16();
base::string16 body = base::string16();
EXPECT_FALSE(credit_card_access_manager_->GetDeletionConfirmationText(
card, &title, &body));
// |title| and |body| should remain unchanged.
EXPECT_EQ(title, base::string16());
EXPECT_EQ(body, base::string16());
}
// Tests retrieving local cards.
TEST_F(CreditCardAccessManagerTest, FetchLocalCardSuccess) {
CreateLocalCard(kTestGUID, kTestNumber);
CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID);
credit_card_access_manager_->PrepareToFetchCreditCard();
WaitForCallbacks();
credit_card_access_manager_->FetchCreditCard(card, accessor_->GetWeakPtr());
EXPECT_TRUE(accessor_->did_succeed());
EXPECT_EQ(ASCIIToUTF16(kTestNumber), accessor_->number());
}
// Ensures that FetchCreditCard() reports a failure when a card does not exist.
TEST_F(CreditCardAccessManagerTest, FetchNullptrFailure) {
personal_data_manager_.ClearCreditCards();
credit_card_access_manager_->PrepareToFetchCreditCard();
WaitForCallbacks();
credit_card_access_manager_->FetchCreditCard(nullptr,
accessor_->GetWeakPtr());
EXPECT_FALSE(accessor_->did_succeed());
}
// Ensures that FetchCreditCard() returns the full PAN upon a successful
// response from payments.
TEST_F(CreditCardAccessManagerTest, FetchServerCardCVCSuccess) {
CreateServerCard(kTestGUID, kTestNumber);
CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID);
credit_card_access_manager_->PrepareToFetchCreditCard();
WaitForCallbacks();
credit_card_access_manager_->FetchCreditCard(card, accessor_->GetWeakPtr());
EXPECT_TRUE(GetRealPanForCVCAuth(AutofillClient::SUCCESS, kTestNumber));
EXPECT_TRUE(accessor_->did_succeed());
EXPECT_EQ(ASCIIToUTF16(kTestNumber), accessor_->number());
}
// Ensures that FetchCreditCard() returns a failure upon a negative response
// from the server.
TEST_F(CreditCardAccessManagerTest, FetchServerCardCVCNetworkError) {
CreateServerCard(kTestGUID, kTestNumber);
CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID);
credit_card_access_manager_->PrepareToFetchCreditCard();
WaitForCallbacks();
credit_card_access_manager_->FetchCreditCard(card, accessor_->GetWeakPtr());
EXPECT_TRUE(
GetRealPanForCVCAuth(AutofillClient::NETWORK_ERROR, std::string()));
EXPECT_FALSE(accessor_->did_succeed());
}
// Ensures that FetchCreditCard() returns a failure upon a negative response
// from the server.
TEST_F(CreditCardAccessManagerTest, FetchServerCardCVCPermanentFailure) {
CreateServerCard(kTestGUID, kTestNumber);
CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID);
credit_card_access_manager_->PrepareToFetchCreditCard();
WaitForCallbacks();
credit_card_access_manager_->FetchCreditCard(card, accessor_->GetWeakPtr());
EXPECT_TRUE(
GetRealPanForCVCAuth(AutofillClient::PERMANENT_FAILURE, std::string()));
EXPECT_FALSE(accessor_->did_succeed());
}
// Ensures that a "try again" response from payments does not end the flow.
TEST_F(CreditCardAccessManagerTest, FetchServerCardCVCTryAgainFailure) {
CreateServerCard(kTestGUID, kTestNumber);
CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID);
credit_card_access_manager_->FetchCreditCard(card, accessor_->GetWeakPtr());
EXPECT_TRUE(
GetRealPanForCVCAuth(AutofillClient::TRY_AGAIN_FAILURE, std::string()));
EXPECT_FALSE(accessor_->did_succeed());
EXPECT_TRUE(GetRealPanForCVCAuth(AutofillClient::SUCCESS, kTestNumber));
EXPECT_TRUE(accessor_->did_succeed());
EXPECT_EQ(ASCIIToUTF16(kTestNumber), accessor_->number());
}
// Ensures that CardUnmaskPreflightCalled metrics are logged correctly.
TEST_F(CreditCardAccessManagerTest, CardUnmaskPreflightCalledMetric) {
std::string preflight_call_metric =
"Autofill.BetterAuth.CardUnmaskPreflightCalled";
{
// Create local card and set user as eligible for FIDO auth.
base::HistogramTester histogram_tester;
CreateLocalCard(kTestGUID, kTestNumber);
#if !defined(OS_IOS)
GetFIDOAuthenticator()->SetUserVerifiable(true);
#endif
ResetPreflightCallLimiter();
credit_card_access_manager_->PrepareToFetchCreditCard();
InvokeUnmaskDetailsTimeout();
WaitForCallbacks();
// If only local cards are available, then no preflight call is made.
histogram_tester.ExpectTotalCount(preflight_call_metric, 0);
}
{
// Create server card and set user as ineligible for FIDO auth.
base::HistogramTester histogram_tester;
CreateServerCard(kTestGUID, kTestNumber);
#if !defined(OS_IOS)
GetFIDOAuthenticator()->SetUserVerifiable(false);
#endif
ResetPreflightCallLimiter();
credit_card_access_manager_->PrepareToFetchCreditCard();
InvokeUnmaskDetailsTimeout();
WaitForCallbacks();
// If user is not verifiable, then no preflight call is made.
histogram_tester.ExpectTotalCount(preflight_call_metric, 0);
}
{
// Create server card and set user as eligible for FIDO auth.
base::HistogramTester histogram_tester;
CreateServerCard(kTestGUID, kTestNumber);
#if !defined(OS_IOS)
GetFIDOAuthenticator()->SetUserVerifiable(true);
#endif
ResetPreflightCallLimiter();
credit_card_access_manager_->PrepareToFetchCreditCard();
InvokeUnmaskDetailsTimeout();
WaitForCallbacks();
// Preflight call is made only if a server card is available and the user is
// eligible for FIDO authentication, except on iOS.
#if defined(OS_IOS)
histogram_tester.ExpectTotalCount(preflight_call_metric, 0);
#else
histogram_tester.ExpectTotalCount(preflight_call_metric, 1);
#endif
}
}
#if !defined(OS_IOS)
// Ensures that FetchCreditCard() returns the full PAN upon a successful
// WebAuthn verification and response from payments.
TEST_F(CreditCardAccessManagerTest, FetchServerCardFIDOSuccess) {
CreateServerCard(kTestGUID, kTestNumber);
CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID);
GetFIDOAuthenticator()->SetUserVerifiable(true);
SetUserOptedIn(true);
payments_client_->AddFidoEligibleCard(card->server_id(), kCredentialId,
kGooglePaymentsRpid);
credit_card_access_manager_->PrepareToFetchCreditCard();
WaitForCallbacks();
credit_card_access_manager_->FetchCreditCard(card, accessor_->GetWeakPtr());
WaitForCallbacks();
// FIDO Success.
EXPECT_EQ(CreditCardFIDOAuthenticator::Flow::AUTHENTICATION_FLOW,
GetFIDOAuthenticator()->current_flow());
TestCreditCardFIDOAuthenticator::GetAssertion(GetFIDOAuthenticator(),
/*did_succeed=*/true);
EXPECT_TRUE(GetRealPanForFIDOAuth(AutofillClient::SUCCESS, kTestNumber));
EXPECT_TRUE(accessor_->did_succeed());
EXPECT_EQ(kCredentialId,
BytesToBase64(GetFIDOAuthenticator()->GetCredentialId()));
EXPECT_EQ(ASCIIToUTF16(kTestNumber), accessor_->number());
}
// Ensures that CVC prompt is invoked after WebAuthn fails.
TEST_F(CreditCardAccessManagerTest, FetchServerCardFIDOFailureCVCFallback) {
CreateServerCard(kTestGUID, kTestNumber);
CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID);
GetFIDOAuthenticator()->SetUserVerifiable(true);
SetUserOptedIn(true);
payments_client_->AddFidoEligibleCard(card->server_id(), kCredentialId,
kGooglePaymentsRpid);
credit_card_access_manager_->PrepareToFetchCreditCard();
WaitForCallbacks();
credit_card_access_manager_->FetchCreditCard(card, accessor_->GetWeakPtr());
WaitForCallbacks();
// FIDO Failure.
EXPECT_EQ(CreditCardFIDOAuthenticator::Flow::AUTHENTICATION_FLOW,
GetFIDOAuthenticator()->current_flow());
TestCreditCardFIDOAuthenticator::GetAssertion(GetFIDOAuthenticator(),
/*did_succeed=*/false);
EXPECT_FALSE(GetRealPanForFIDOAuth(AutofillClient::SUCCESS, kTestNumber));
EXPECT_FALSE(accessor_->did_succeed());
// Followed by a fallback to CVC.
EXPECT_EQ(CreditCardFIDOAuthenticator::Flow::NONE_FLOW,
GetFIDOAuthenticator()->current_flow());
EXPECT_TRUE(GetRealPanForCVCAuth(AutofillClient::SUCCESS, kTestNumber));
EXPECT_TRUE(accessor_->did_succeed());
EXPECT_EQ(ASCIIToUTF16(kTestNumber), accessor_->number());
}
// Ensures WebAuthn call is not made if Request Options is missing a Credential
// ID, and falls back to CVC.
TEST_F(CreditCardAccessManagerTest,
FetchServerCardBadRequestOptionsCVCFallback) {
CreateServerCard(kTestGUID, kTestNumber);
CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID);
GetFIDOAuthenticator()->SetUserVerifiable(true);
SetUserOptedIn(true);
// Don't set Credential ID.
payments_client_->AddFidoEligibleCard(card->server_id(), /*credential_id=*/"",
kGooglePaymentsRpid);
credit_card_access_manager_->PrepareToFetchCreditCard();
WaitForCallbacks();
credit_card_access_manager_->FetchCreditCard(card, accessor_->GetWeakPtr());
WaitForCallbacks();
// FIDO Failure.
EXPECT_FALSE(GetRealPanForFIDOAuth(AutofillClient::SUCCESS, kTestNumber));
EXPECT_FALSE(accessor_->did_succeed());
// Followed by a fallback to CVC.
EXPECT_TRUE(GetRealPanForCVCAuth(AutofillClient::SUCCESS, kTestNumber));
EXPECT_TRUE(accessor_->did_succeed());
EXPECT_EQ(ASCIIToUTF16(kTestNumber), accessor_->number());
}
// Ensures that CVC prompt is invoked when the pre-flight call to Google
// Payments times out.
TEST_F(CreditCardAccessManagerTest, FetchServerCardFIDOTimeoutCVCFallback) {
CreateServerCard(kTestGUID, kTestNumber);
CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID);
GetFIDOAuthenticator()->SetUserVerifiable(true);
SetUserOptedIn(true);
credit_card_access_manager_->FetchCreditCard(card, accessor_->GetWeakPtr());
InvokeUnmaskDetailsTimeout();
WaitForCallbacks();
EXPECT_TRUE(GetRealPanForCVCAuth(AutofillClient::SUCCESS, kTestNumber));
EXPECT_TRUE(accessor_->did_succeed());
EXPECT_EQ(ASCIIToUTF16(kTestNumber), accessor_->number());
}
#if defined(OS_ANDROID)
// Ensures that the WebAuthn enrollment prompt is invoked after user opts in.
TEST_F(CreditCardAccessManagerTest, FIDOEnrollmentSuccess_Android) {
CreateServerCard(kTestGUID, kTestNumber);
CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID);
GetFIDOAuthenticator()->SetUserVerifiable(true);
SetUserOptedIn(false);
credit_card_access_manager_->FetchCreditCard(card, accessor_->GetWeakPtr());
InvokeUnmaskDetailsTimeout();
WaitForCallbacks();
EXPECT_TRUE(GetRealPanForCVCAuth(AutofillClient::SUCCESS, kTestNumber,
/*fido_opt_in=*/true));
WaitForCallbacks();
// 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(AutofillClient::SUCCESS, true);
EXPECT_EQ(kGooglePaymentsRpid, GetFIDOAuthenticator()->GetRelyingPartyId());
EXPECT_EQ(kTestChallenge,
BytesToBase64(GetFIDOAuthenticator()->GetChallenge()));
EXPECT_TRUE(GetFIDOAuthenticator()->IsUserOptedIn());
}
// Ensures that the failed user verification disallows enrollment.
TEST_F(CreditCardAccessManagerTest, FIDOEnrollmentUserVerificationFailure) {
CreateServerCard(kTestGUID, kTestNumber);
CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID);
GetFIDOAuthenticator()->SetUserVerifiable(true);
SetUserOptedIn(false);
credit_card_access_manager_->FetchCreditCard(card, accessor_->GetWeakPtr());
InvokeUnmaskDetailsTimeout();
WaitForCallbacks();
EXPECT_TRUE(GetRealPanForCVCAuth(AutofillClient::SUCCESS, kTestNumber,
/*fido_opt_in=*/true));
// Mock user response.
TestCreditCardFIDOAuthenticator::MakeCredential(GetFIDOAuthenticator(),
/*did_succeed=*/false);
EXPECT_FALSE(GetFIDOAuthenticator()->IsUserOptedIn());
}
// Ensures that enrollment does not happen if the server returns a failure.
TEST_F(CreditCardAccessManagerTest, FIDOEnrollmentServerFailure) {
CreateServerCard(kTestGUID, kTestNumber);
CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID);
GetFIDOAuthenticator()->SetUserVerifiable(true);
SetUserOptedIn(false);
credit_card_access_manager_->FetchCreditCard(card, accessor_->GetWeakPtr());
InvokeUnmaskDetailsTimeout();
WaitForCallbacks();
EXPECT_TRUE(GetRealPanForCVCAuth(AutofillClient::SUCCESS, kTestNumber,
/*fido_opt_in=*/true));
// Mock user response and OptChange payments call.
TestCreditCardFIDOAuthenticator::MakeCredential(GetFIDOAuthenticator(),
/*did_succeed=*/true);
OptChange(AutofillClient::PERMANENT_FAILURE, false);
EXPECT_FALSE(GetFIDOAuthenticator()->IsUserOptedIn());
}
// Ensures that use of new card invokes authorization flow when user is
// opted-in.
TEST_F(CreditCardAccessManagerTest, FIDONewCardAuthorization) {
CreateServerCard(kTestGUID, kTestNumber);
CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID);
// Opt the user in, but don't include the card above.
std::string other_server_id = "00000000-0000-0000-0000-000000000034";
payments_client_->AddFidoEligibleCard(other_server_id, kCredentialId,
kGooglePaymentsRpid);
GetFIDOAuthenticator()->SetUserVerifiable(true);
SetUserOptedIn(true);
credit_card_access_manager_->PrepareToFetchCreditCard();
WaitForCallbacks();
credit_card_access_manager_->FetchCreditCard(card, accessor_->GetWeakPtr());
InvokeUnmaskDetailsTimeout();
WaitForCallbacks();
EXPECT_TRUE(GetRealPanForCVCAuth(AutofillClient::SUCCESS, kTestNumber,
/*fido_opt_in=*/false,
/*follow_with_fido_auth=*/true));
// Mock user response and OptChange payments call.
EXPECT_EQ(CreditCardFIDOAuthenticator::Flow::FOLLOWUP_AFTER_CVC_AUTH_FLOW,
GetFIDOAuthenticator()->current_flow());
TestCreditCardFIDOAuthenticator::GetAssertion(GetFIDOAuthenticator(),
/*did_succeed=*/true);
OptChange(AutofillClient::SUCCESS, true);
EXPECT_TRUE(GetFIDOAuthenticator()->IsUserOptedIn());
}
#else // defined(OS_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_F(CreditCardAccessManagerTest,
FIDOEnrollmentSuccess_CreationOptions_Desktop) {
ClearStrikes();
CreateServerCard(kTestGUID, kTestNumber);
CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID);
GetFIDOAuthenticator()->SetUserVerifiable(true);
SetUserOptedIn(false);
payments_client_->AllowFidoRegistration(true);
credit_card_access_manager_->FetchCreditCard(card, accessor_->GetWeakPtr());
InvokeUnmaskDetailsTimeout();
WaitForCallbacks();
// Mock user and payments response.
AcceptWebauthnOfferDialog(/*did_accept=*/true);
EXPECT_TRUE(GetRealPanForCVCAuth(AutofillClient::SUCCESS, kTestNumber,
/*fido_opt_in=*/false));
WaitForCallbacks();
OptChange(AutofillClient::SUCCESS, /*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(AutofillClient::SUCCESS, /*user_is_opted_in=*/true);
EXPECT_EQ(kGooglePaymentsRpid, GetFIDOAuthenticator()->GetRelyingPartyId());
EXPECT_EQ(kTestChallenge,
BytesToBase64(GetFIDOAuthenticator()->GetChallenge()));
EXPECT_TRUE(GetFIDOAuthenticator()->IsUserOptedIn());
EXPECT_EQ(0, GetStrikes());
}
// Ensures that the correct number of strikes are added when the user declines
// the WebAuthn offer.
TEST_F(CreditCardAccessManagerTest, FIDOEnrollment_OfferDeclined_Desktop) {
ClearStrikes();
CreateServerCard(kTestGUID, kTestNumber);
CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID);
GetFIDOAuthenticator()->SetUserVerifiable(true);
SetUserOptedIn(false);
payments_client_->AllowFidoRegistration(true);
credit_card_access_manager_->FetchCreditCard(card, accessor_->GetWeakPtr());
InvokeUnmaskDetailsTimeout();
WaitForCallbacks();
// Mock user response.
AcceptWebauthnOfferDialog(/*did_accept=*/false);
EXPECT_EQ(
FidoAuthenticationStrikeDatabase::kStrikesToAddWhenOptInOfferDeclined,
GetStrikes());
}
// Ensures that the correct number of strikes are added when the user fails to
// complete user-verification for an opt-in attempt.
TEST_F(CreditCardAccessManagerTest,
FIDOEnrollment_UserVerificationFailed_Desktop) {
ClearStrikes();
CreateServerCard(kTestGUID, kTestNumber);
CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID);
GetFIDOAuthenticator()->SetUserVerifiable(true);
SetUserOptedIn(false);
payments_client_->AllowFidoRegistration(true);
credit_card_access_manager_->FetchCreditCard(card, accessor_->GetWeakPtr());
InvokeUnmaskDetailsTimeout();
WaitForCallbacks();
// Mock user and payments response.
AcceptWebauthnOfferDialog(/*did_accept=*/true);
EXPECT_TRUE(GetRealPanForCVCAuth(AutofillClient::SUCCESS, kTestNumber,
/*fido_opt_in=*/false));
WaitForCallbacks();
OptChange(AutofillClient::SUCCESS, /*user_is_opted_in=*/false,
/*include_creation_options=*/true);
// Mock user response and OptChange payments call.
TestCreditCardFIDOAuthenticator::MakeCredential(GetFIDOAuthenticator(),
/*did_succeed=*/false);
EXPECT_EQ(FidoAuthenticationStrikeDatabase::
kStrikesToAddWhenUserVerificationFailsOnOptInAttempt,
GetStrikes());
}
// 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_F(CreditCardAccessManagerTest,
FIDOEnrollmentSuccess_RequestOptions_Desktop) {
CreateServerCard(kTestGUID, kTestNumber);
CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID);
GetFIDOAuthenticator()->SetUserVerifiable(true);
SetUserOptedIn(false);
payments_client_->AllowFidoRegistration(true);
credit_card_access_manager_->FetchCreditCard(card, accessor_->GetWeakPtr());
InvokeUnmaskDetailsTimeout();
WaitForCallbacks();
// Mock user and payments response.
AcceptWebauthnOfferDialog(/*did_accept=*/true);
EXPECT_TRUE(GetRealPanForCVCAuth(AutofillClient::SUCCESS, kTestNumber,
/*fido_opt_in=*/false));
WaitForCallbacks();
OptChange(AutofillClient::SUCCESS, /*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(AutofillClient::SUCCESS, /*user_is_opted_in=*/true);
EXPECT_EQ(kGooglePaymentsRpid, GetFIDOAuthenticator()->GetRelyingPartyId());
EXPECT_EQ(kTestChallenge,
BytesToBase64(GetFIDOAuthenticator()->GetChallenge()));
EXPECT_TRUE(GetFIDOAuthenticator()->IsUserOptedIn());
}
#endif // defined(OS_ANDROID)
#endif // !defined(OS_IOS)
// Ensures that |is_authentication_in_progress_| is set correctly.
TEST_F(CreditCardAccessManagerTest, AuthenticationInProgress) {
CreateServerCard(kTestGUID, kTestNumber);
CreditCard* card = credit_card_access_manager_->GetCreditCard(kTestGUID);
EXPECT_FALSE(IsAuthenticationInProgress());
credit_card_access_manager_->FetchCreditCard(card, accessor_->GetWeakPtr());
EXPECT_TRUE(IsAuthenticationInProgress());
EXPECT_TRUE(GetRealPanForCVCAuth(AutofillClient::SUCCESS, kTestNumber));
EXPECT_FALSE(IsAuthenticationInProgress());
}
// TODO(crbug/949269): Once metrics are added, create test to ensure that
// PrepareToFetchCreditCard() is properly rate limited.
} // namespace autofill