Commit 914686c5 authored by Rouslan Solomakhin's avatar Rouslan Solomakhin Committed by Commit Bot

[Web Payment] Invoke WebAuthn from SecurePaymentConfirmationApp.

Before this patch, paying via secure payment confirmation method would
always return {"status": "success"} without invoking WebAuthn.

This patch checks that the platform authenticator is user-verifying and
invokes WebAuthn with stubbed out options. The
autofill::InternalAuthenticator is being used because it provides a
cross-platform abstraction that fits neatly into the cross-platform
payment apps and factories of Web Payment.

After this patch, only devices with user-verifying authenticators would
display a payment app and invoking that app will always fail because of
the stub credential identifier.

Design: https://bit.ly/secure-payment-confirmation
Explainer: https://github.com/rsolomakhin/secure-payment-confirmation

Bug: 1110320
Change-Id: I8adabf2938ae5a65a6a87c9bc5f6df275b833f24
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2364813Reviewed-by: default avatarMike West <mkwst@chromium.org>
Reviewed-by: default avatarKen Buchanan <kenrb@chromium.org>
Reviewed-by: default avatarNick Burris <nburris@chromium.org>
Commit-Queue: Rouslan Solomakhin <rouslan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#800572}
parent 84b34257
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include "base/check_op.h" #include "base/check_op.h"
#include "base/memory/singleton.h" #include "base/memory/singleton.h"
#include "base/notreached.h" #include "base/notreached.h"
#include "chrome/browser/autofill/android/internal_authenticator_android.h"
#include "chrome/browser/payments/android/jni_headers/PaymentAppServiceBridge_jni.h" #include "chrome/browser/payments/android/jni_headers/PaymentAppServiceBridge_jni.h"
#include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile.h"
#include "chrome/browser/web_data_service_factory.h" #include "chrome/browser/web_data_service_factory.h"
...@@ -246,6 +247,11 @@ PaymentAppServiceBridge::GetMethodData() const { ...@@ -246,6 +247,11 @@ PaymentAppServiceBridge::GetMethodData() const {
return spec_->method_data(); return spec_->method_data();
} }
std::unique_ptr<autofill::InternalAuthenticator>
PaymentAppServiceBridge::CreateInternalAuthenticator() const {
return std::make_unique<InternalAuthenticatorAndroid>(render_frame_host_);
}
scoped_refptr<PaymentManifestWebDataService> scoped_refptr<PaymentManifestWebDataService>
PaymentAppServiceBridge::GetPaymentManifestWebDataService() const { PaymentAppServiceBridge::GetPaymentManifestWebDataService() const {
return payment_manifest_web_data_service_; return payment_manifest_web_data_service_;
......
...@@ -68,6 +68,8 @@ class PaymentAppServiceBridge : public PaymentAppFactory::Delegate { ...@@ -68,6 +68,8 @@ class PaymentAppServiceBridge : public PaymentAppFactory::Delegate {
content::RenderFrameHost* GetInitiatorRenderFrameHost() const override; content::RenderFrameHost* GetInitiatorRenderFrameHost() const override;
const std::vector<mojom::PaymentMethodDataPtr>& GetMethodData() const std::vector<mojom::PaymentMethodDataPtr>& GetMethodData()
const override; const override;
std::unique_ptr<autofill::InternalAuthenticator> CreateInternalAuthenticator()
const override;
scoped_refptr<PaymentManifestWebDataService> scoped_refptr<PaymentManifestWebDataService>
GetPaymentManifestWebDataService() const override; GetPaymentManifestWebDataService() const override;
bool MayCrawlForInstallablePaymentApps() override; bool MayCrawlForInstallablePaymentApps() override;
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/views/payments/payment_request_dialog_view.h" #include "chrome/browser/ui/views/payments/payment_request_dialog_view.h"
#include "chrome/browser/web_data_service_factory.h" #include "chrome/browser/web_data_service_factory.h"
#include "components/autofill/content/browser/webauthn/internal_authenticator_impl.h"
#include "components/autofill/core/browser/address_normalizer_impl.h" #include "components/autofill/core/browser/address_normalizer_impl.h"
#include "components/autofill/core/browser/geo/region_data_loader_impl.h" #include "components/autofill/core/browser/geo/region_data_loader_impl.h"
#include "components/autofill/core/browser/personal_data_manager.h" #include "components/autofill/core/browser/personal_data_manager.h"
...@@ -167,6 +168,12 @@ bool ChromePaymentRequestDelegate::IsBrowserWindowActive() const { ...@@ -167,6 +168,12 @@ bool ChromePaymentRequestDelegate::IsBrowserWindowActive() const {
return browser && browser->window() && browser->window()->IsActive(); return browser && browser->window() && browser->window()->IsActive();
} }
std::unique_ptr<autofill::InternalAuthenticator>
ChromePaymentRequestDelegate::CreateInternalAuthenticator(
content::RenderFrameHost* rfh) const {
return std::make_unique<content::InternalAuthenticatorImpl>(rfh);
}
scoped_refptr<PaymentManifestWebDataService> scoped_refptr<PaymentManifestWebDataService>
ChromePaymentRequestDelegate::GetPaymentManifestWebDataService() const { ChromePaymentRequestDelegate::GetPaymentManifestWebDataService() const {
return WebDataServiceFactory::GetPaymentManifestWebDataForProfile( return WebDataServiceFactory::GetPaymentManifestWebDataForProfile(
......
...@@ -47,6 +47,8 @@ class ChromePaymentRequestDelegate : public ContentPaymentRequestDelegate { ...@@ -47,6 +47,8 @@ class ChromePaymentRequestDelegate : public ContentPaymentRequestDelegate {
bool IsBrowserWindowActive() const override; bool IsBrowserWindowActive() const override;
// ContentPaymentRequestDelegate: // ContentPaymentRequestDelegate:
std::unique_ptr<autofill::InternalAuthenticator> CreateInternalAuthenticator(
content::RenderFrameHost* rfh) const override;
scoped_refptr<PaymentManifestWebDataService> scoped_refptr<PaymentManifestWebDataService>
GetPaymentManifestWebDataService() const override; GetPaymentManifestWebDataService() const override;
PaymentRequestDisplayManager* GetDisplayManager() override; PaymentRequestDisplayManager* GetDisplayManager() override;
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
// found in the LICENSE file. // found in the LICENSE file.
#include "base/command_line.h" #include "base/command_line.h"
#include "build/build_config.h"
#include "chrome/test/payments/payment_request_platform_browsertest_base.h" #include "chrome/test/payments/payment_request_platform_browsertest_base.h"
#include "content/public/common/content_switches.h" #include "content/public/common/content_switches.h"
#include "content/public/test/browser_test.h" #include "content/public/test/browser_test.h"
...@@ -33,7 +34,26 @@ class SecurePaymentConfirmationTest ...@@ -33,7 +34,26 @@ class SecurePaymentConfirmationTest
} }
}; };
IN_PROC_BROWSER_TEST_F(SecurePaymentConfirmationTest, PaymentSheetShowsApp) { IN_PROC_BROWSER_TEST_F(SecurePaymentConfirmationTest, NoAuthenticator) {
test_controller()->SetHasAuthenticator(false);
NavigateTo("a.com", "/payment_handler_status.html");
// EvalJs waits for JavaScript promise to resolve.
EXPECT_EQ(
"The payment method \"secure-payment-confirmation\" is not supported.",
content::EvalJs(GetActiveWebContents(), kInvokePaymentRequest));
}
#if defined(OS_ANDROID)
// TODO(https://crbug.com/1110320): Implement SetHasAuthenticator() for Android,
// so this behavior can be tested on Android as well.
#define MAYBE_PaymentSheetShowsApp DISABLED_PaymentSheetShowsApp
#else
#define MAYBE_PaymentSheetShowsApp PaymentSheetShowsApp
#endif // OS_ANDROID
IN_PROC_BROWSER_TEST_F(SecurePaymentConfirmationTest,
MAYBE_PaymentSheetShowsApp) {
test_controller()->SetHasAuthenticator(true);
NavigateTo("a.com", "/payment_handler_status.html"); NavigateTo("a.com", "/payment_handler_status.html");
ResetEventWaiterForSingleEvent(TestEvent::kAppListReady); ResetEventWaiterForSingleEvent(TestEvent::kAppListReady);
...@@ -54,6 +74,7 @@ class SecurePaymentConfirmationDisabledTest ...@@ -54,6 +74,7 @@ class SecurePaymentConfirmationDisabledTest
IN_PROC_BROWSER_TEST_F(SecurePaymentConfirmationDisabledTest, IN_PROC_BROWSER_TEST_F(SecurePaymentConfirmationDisabledTest,
PaymentMethodNotSupported) { PaymentMethodNotSupported) {
test_controller()->SetHasAuthenticator(true);
NavigateTo("a.com", "/payment_handler_status.html"); NavigateTo("a.com", "/payment_handler_status.html");
// EvalJs waits for JavaScript promise to resolve. // EvalJs waits for JavaScript promise to resolve.
......
...@@ -41,6 +41,9 @@ static_library("test_support") { ...@@ -41,6 +41,9 @@ static_library("test_support") {
if (!is_android) { if (!is_android) {
sources += [ "payment_request_test_controller_desktop.cc" ] sources += [ "payment_request_test_controller_desktop.cc" ]
deps += [ "//chrome/test:test_support_ui" ] deps += [
"//chrome/test:test_support_ui",
"//components/autofill/content/browser/webauthn",
]
} }
} }
include_rules = [ include_rules = [
"+components/network_session_configurator/common", "+components/network_session_configurator/common",
"+components/payments", "+components/payments",
"+third_party/blink/public/mojom/webauthn",
] ]
...@@ -66,6 +66,7 @@ class PaymentRequestTestController { ...@@ -66,6 +66,7 @@ class PaymentRequestTestController {
void SetValidSsl(bool valid_ssl); void SetValidSsl(bool valid_ssl);
void SetCanMakePaymentEnabledPref(bool can_make_payment_enabled); void SetCanMakePaymentEnabledPref(bool can_make_payment_enabled);
void SetTwaPackageName(const std::string& twa_package_name); void SetTwaPackageName(const std::string& twa_package_name);
void SetHasAuthenticator(bool has_authenticator);
void SetTwaPaymentApp(const std::string& method_name, void SetTwaPaymentApp(const std::string& method_name,
const std::string& response); const std::string& response);
...@@ -122,6 +123,7 @@ class PaymentRequestTestController { ...@@ -122,6 +123,7 @@ class PaymentRequestTestController {
bool valid_ssl_ = true; bool valid_ssl_ = true;
bool can_make_payment_pref_ = true; bool can_make_payment_pref_ = true;
std::string twa_package_name_; std::string twa_package_name_;
bool has_authenticator_ = false;
std::string twa_payment_app_method_name_; std::string twa_payment_app_method_name_;
std::string twa_payment_app_response_; std::string twa_payment_app_response_;
std::vector<AppDescription> app_descriptions_; std::vector<AppDescription> app_descriptions_;
......
...@@ -112,6 +112,13 @@ void PaymentRequestTestController::SetTwaPackageName( ...@@ -112,6 +112,13 @@ void PaymentRequestTestController::SetTwaPackageName(
/*skip_ui_for_basic_card=*/false, twa_package_name_); /*skip_ui_for_basic_card=*/false, twa_package_name_);
} }
void PaymentRequestTestController::SetHasAuthenticator(bool has_authenticator) {
// TODO(https://crbug.com/1110320): Implement SetHasAuthenticator() for
// Android, so secure payment confirmation can be integration tested on
// Android as well.
has_authenticator_ = has_authenticator;
}
void PaymentRequestTestController::SetTwaPaymentApp( void PaymentRequestTestController::SetTwaPaymentApp(
const std::string& method_name, const std::string& method_name,
const std::string& response) { const std::string& response) {
......
...@@ -10,6 +10,8 @@ ...@@ -10,6 +10,8 @@
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
#include "chrome/browser/payments/chrome_payment_request_delegate.h" #include "chrome/browser/payments/chrome_payment_request_delegate.h"
#include "chrome/browser/payments/payment_request_factory.h" #include "chrome/browser/payments/payment_request_factory.h"
#include "components/autofill/content/browser/webauthn/internal_authenticator_impl.h"
#include "components/autofill/core/browser/payments/internal_authenticator.h"
#include "components/payments/content/android_app_communication.h" #include "components/payments/content/android_app_communication.h"
#include "components/payments/content/payment_request.h" #include "components/payments/content/payment_request.h"
#include "components/payments/content/payment_request_web_contents_manager.h" #include "components/payments/content/payment_request_web_contents_manager.h"
...@@ -17,22 +19,46 @@ ...@@ -17,22 +19,46 @@
#include "components/payments/core/payment_request_delegate.h" #include "components/payments/core/payment_request_delegate.h"
#include "components/sync_preferences/testing_pref_service_syncable.h" #include "components/sync_preferences/testing_pref_service_syncable.h"
#include "mojo/public/cpp/bindings/pending_receiver.h" #include "mojo/public/cpp/bindings/pending_receiver.h"
#include "third_party/blink/public/mojom/webauthn/authenticator.mojom.h"
namespace payments { namespace payments {
namespace { namespace {
class TestAuthenticator : public content::InternalAuthenticatorImpl {
public:
explicit TestAuthenticator(content::RenderFrameHost* rfh,
bool has_authenticator)
: content::InternalAuthenticatorImpl(rfh),
has_authenticator_(has_authenticator) {}
~TestAuthenticator() override = default;
// autofill::InternalAuthenticator
void IsUserVerifyingPlatformAuthenticatorAvailable(
blink::mojom::Authenticator::
IsUserVerifyingPlatformAuthenticatorAvailableCallback callback)
override {
std::move(callback).Run(has_authenticator_);
}
private:
const bool has_authenticator_;
};
class ChromePaymentRequestTestDelegate : public ChromePaymentRequestDelegate { class ChromePaymentRequestTestDelegate : public ChromePaymentRequestDelegate {
public: public:
ChromePaymentRequestTestDelegate(content::WebContents* web_contents, ChromePaymentRequestTestDelegate(content::WebContents* web_contents,
bool is_off_the_record, bool is_off_the_record,
bool valid_ssl, bool valid_ssl,
PrefService* prefs, PrefService* prefs,
const std::string& twa_package_name) const std::string& twa_package_name,
bool has_authenticator)
: ChromePaymentRequestDelegate(web_contents), : ChromePaymentRequestDelegate(web_contents),
is_off_the_record_(is_off_the_record), is_off_the_record_(is_off_the_record),
valid_ssl_(valid_ssl), valid_ssl_(valid_ssl),
prefs_(prefs), prefs_(prefs),
twa_package_name_(twa_package_name) {} twa_package_name_(twa_package_name),
has_authenticator_(has_authenticator) {}
bool IsOffTheRecord() const override { return is_off_the_record_; } bool IsOffTheRecord() const override { return is_off_the_record_; }
std::string GetInvalidSslCertificateErrorMessage() override { std::string GetInvalidSslCertificateErrorMessage() override {
...@@ -41,12 +67,17 @@ class ChromePaymentRequestTestDelegate : public ChromePaymentRequestDelegate { ...@@ -41,12 +67,17 @@ class ChromePaymentRequestTestDelegate : public ChromePaymentRequestDelegate {
PrefService* GetPrefService() override { return prefs_; } PrefService* GetPrefService() override { return prefs_; }
bool IsBrowserWindowActive() const override { return true; } bool IsBrowserWindowActive() const override { return true; }
std::string GetTwaPackageName() const override { return twa_package_name_; } std::string GetTwaPackageName() const override { return twa_package_name_; }
std::unique_ptr<autofill::InternalAuthenticator> CreateInternalAuthenticator(
content::RenderFrameHost* rfh) const override {
return std::make_unique<TestAuthenticator>(rfh, has_authenticator_);
}
private: private:
const bool is_off_the_record_; const bool is_off_the_record_;
const bool valid_ssl_; const bool valid_ssl_;
PrefService* const prefs_; PrefService* const prefs_;
const std::string twa_package_name_; const std::string twa_package_name_;
const bool has_authenticator_;
}; };
} // namespace } // namespace
...@@ -159,6 +190,11 @@ void PaymentRequestTestController::SetTwaPackageName( ...@@ -159,6 +190,11 @@ void PaymentRequestTestController::SetTwaPackageName(
UpdateDelegateFactory(); UpdateDelegateFactory();
} }
void PaymentRequestTestController::SetHasAuthenticator(bool has_authenticator) {
has_authenticator_ = has_authenticator;
UpdateDelegateFactory();
}
void PaymentRequestTestController::SetTwaPaymentApp( void PaymentRequestTestController::SetTwaPaymentApp(
const std::string& method_name, const std::string& method_name,
const std::string& response) { const std::string& response) {
...@@ -171,7 +207,7 @@ void PaymentRequestTestController::UpdateDelegateFactory() { ...@@ -171,7 +207,7 @@ void PaymentRequestTestController::UpdateDelegateFactory() {
SetPaymentRequestFactoryForTesting(base::BindRepeating( SetPaymentRequestFactoryForTesting(base::BindRepeating(
[](PaymentRequest::ObserverForTest* observer_for_test, [](PaymentRequest::ObserverForTest* observer_for_test,
bool is_off_the_record, bool valid_ssl, PrefService* prefs, bool is_off_the_record, bool valid_ssl, PrefService* prefs,
const std::string& twa_package_name, const std::string& twa_package_name, bool has_authenticator,
const std::string& twa_payment_app_method_name, const std::string& twa_payment_app_method_name,
const std::string& twa_payment_app_response, const std::string& twa_payment_app_response,
mojo::PendingReceiver<payments::mojom::PaymentRequest> receiver, mojo::PendingReceiver<payments::mojom::PaymentRequest> receiver,
...@@ -180,8 +216,8 @@ void PaymentRequestTestController::UpdateDelegateFactory() { ...@@ -180,8 +216,8 @@ void PaymentRequestTestController::UpdateDelegateFactory() {
content::WebContents::FromRenderFrameHost(render_frame_host); content::WebContents::FromRenderFrameHost(render_frame_host);
DCHECK(web_contents); DCHECK(web_contents);
auto delegate = std::make_unique<ChromePaymentRequestTestDelegate>( auto delegate = std::make_unique<ChromePaymentRequestTestDelegate>(
web_contents, is_off_the_record, valid_ssl, prefs, web_contents, is_off_the_record, valid_ssl, prefs, twa_package_name,
twa_package_name); has_authenticator);
PaymentRequestWebContentsManager* manager = PaymentRequestWebContentsManager* manager =
PaymentRequestWebContentsManager::GetOrCreateForWebContents( PaymentRequestWebContentsManager::GetOrCreateForWebContents(
web_contents); web_contents);
...@@ -196,7 +232,7 @@ void PaymentRequestTestController::UpdateDelegateFactory() { ...@@ -196,7 +232,7 @@ void PaymentRequestTestController::UpdateDelegateFactory() {
observer_for_test); observer_for_test);
}, },
observer_converter_.get(), is_off_the_record_, valid_ssl_, prefs_.get(), observer_converter_.get(), is_off_the_record_, valid_ssl_, prefs_.get(),
twa_package_name_, twa_payment_app_method_name_, twa_package_name_, has_authenticator_, twa_payment_app_method_name_,
twa_payment_app_response_)); twa_payment_app_response_));
} }
......
...@@ -62,6 +62,7 @@ static_library("content") { ...@@ -62,6 +62,7 @@ static_library("content") {
"//components/ukm/content", "//components/ukm/content",
"//components/url_formatter", "//components/url_formatter",
"//content/public/browser", "//content/public/browser",
"//device/fido",
"//third_party/blink/public:blink_headers", "//third_party/blink/public:blink_headers",
"//url", "//url",
] ]
......
...@@ -10,6 +10,7 @@ include_rules = [ ...@@ -10,6 +10,7 @@ include_rules = [
"+components/url_formatter", "+components/url_formatter",
"+components/webdata/common", "+components/webdata/common",
"+content/public", "+content/public",
"+device/fido",
"+mojo/public/cpp", "+mojo/public/cpp",
"+net", "+net",
"+services/data_decoder/public/cpp", "+services/data_decoder/public/cpp",
...@@ -21,6 +22,7 @@ include_rules = [ ...@@ -21,6 +22,7 @@ include_rules = [
"+third_party/blink/public/common", "+third_party/blink/public/common",
"+third_party/blink/public/mojom/devtools/console_message.mojom.h", "+third_party/blink/public/mojom/devtools/console_message.mojom.h",
"+third_party/blink/public/mojom/payments", "+third_party/blink/public/mojom/payments",
"+third_party/blink/public/mojom/webauthn",
"+third_party/blink/public/platform/modules/payments", "+third_party/blink/public/platform/modules/payments",
"+third_party/skia/include/core/SkBitmap.h", "+third_party/skia/include/core/SkBitmap.h",
"+ui/base", "+ui/base",
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include "base/memory/weak_ptr.h" #include "base/memory/weak_ptr.h"
#include "base/stl_util.h" #include "base/stl_util.h"
#include "components/autofill/core/browser/payments/internal_authenticator.h"
#include "components/payments/content/android_app_communication.h" #include "components/payments/content/android_app_communication.h"
#include "components/payments/content/android_app_communication_test_support.h" #include "components/payments/content/android_app_communication_test_support.h"
#include "components/payments/content/payment_app_factory.h" #include "components/payments/content/payment_app_factory.h"
...@@ -64,6 +65,8 @@ class MockPaymentAppFactoryDelegate : public PaymentAppFactory::Delegate { ...@@ -64,6 +65,8 @@ class MockPaymentAppFactoryDelegate : public PaymentAppFactory::Delegate {
MOCK_CONST_METHOD0(GetInitiatorRenderFrameHost, content::RenderFrameHost*()); MOCK_CONST_METHOD0(GetInitiatorRenderFrameHost, content::RenderFrameHost*());
MOCK_CONST_METHOD0(GetMethodData, MOCK_CONST_METHOD0(GetMethodData,
const std::vector<mojom::PaymentMethodDataPtr>&()); const std::vector<mojom::PaymentMethodDataPtr>&());
MOCK_CONST_METHOD0(CreateInternalAuthenticator,
std::unique_ptr<autofill::InternalAuthenticator>());
MOCK_CONST_METHOD0(GetPaymentManifestWebDataService, MOCK_CONST_METHOD0(GetPaymentManifestWebDataService,
scoped_refptr<PaymentManifestWebDataService>()); scoped_refptr<PaymentManifestWebDataService>());
MOCK_METHOD0(MayCrawlForInstallablePaymentApps, bool()); MOCK_METHOD0(MayCrawlForInstallablePaymentApps, bool());
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#ifndef COMPONENTS_PAYMENTS_CONTENT_CONTENT_PAYMENT_REQUEST_DELEGATE_H_ #ifndef COMPONENTS_PAYMENTS_CONTENT_CONTENT_PAYMENT_REQUEST_DELEGATE_H_
#define COMPONENTS_PAYMENTS_CONTENT_CONTENT_PAYMENT_REQUEST_DELEGATE_H_ #define COMPONENTS_PAYMENTS_CONTENT_CONTENT_PAYMENT_REQUEST_DELEGATE_H_
#include <memory>
#include <string> #include <string>
#include "components/payments/content/payment_request_display_manager.h" #include "components/payments/content/payment_request_display_manager.h"
...@@ -13,6 +14,14 @@ ...@@ -13,6 +14,14 @@
template <class T> template <class T>
class scoped_refptr; class scoped_refptr;
namespace autofill {
class InternalAuthenticator;
} // namespace autofill
namespace content {
class RenderFrameHost;
} // namespace content
namespace payments { namespace payments {
class PaymentManifestWebDataService; class PaymentManifestWebDataService;
...@@ -23,6 +32,11 @@ class ContentPaymentRequestDelegate : public PaymentRequestDelegate { ...@@ -23,6 +32,11 @@ class ContentPaymentRequestDelegate : public PaymentRequestDelegate {
public: public:
~ContentPaymentRequestDelegate() override {} ~ContentPaymentRequestDelegate() override {}
// Creates and returns an instance of the InternalAuthenticator interface for
// communication with WebAuthn.
virtual std::unique_ptr<autofill::InternalAuthenticator>
CreateInternalAuthenticator(content::RenderFrameHost* rfh) const = 0;
// Returns the web data service for caching payment method manifests. // Returns the web data service for caching payment method manifests.
virtual scoped_refptr<PaymentManifestWebDataService> virtual scoped_refptr<PaymentManifestWebDataService>
GetPaymentManifestWebDataService() const = 0; GetPaymentManifestWebDataService() const = 0;
......
...@@ -20,6 +20,7 @@ class GURL; ...@@ -20,6 +20,7 @@ class GURL;
namespace autofill { namespace autofill {
class AutofillProfile; class AutofillProfile;
class InternalAuthenticator;
} // namespace autofill } // namespace autofill
namespace content { namespace content {
...@@ -51,6 +52,8 @@ class PaymentAppFactory { ...@@ -51,6 +52,8 @@ class PaymentAppFactory {
virtual content::RenderFrameHost* GetInitiatorRenderFrameHost() const = 0; virtual content::RenderFrameHost* GetInitiatorRenderFrameHost() const = 0;
virtual const std::vector<mojom::PaymentMethodDataPtr>& GetMethodData() virtual const std::vector<mojom::PaymentMethodDataPtr>& GetMethodData()
const = 0; const = 0;
virtual std::unique_ptr<autofill::InternalAuthenticator>
CreateInternalAuthenticator() const = 0;
virtual scoped_refptr<PaymentManifestWebDataService> virtual scoped_refptr<PaymentManifestWebDataService>
GetPaymentManifestWebDataService() const = 0; GetPaymentManifestWebDataService() const = 0;
virtual bool MayCrawlForInstallablePaymentApps() = 0; virtual bool MayCrawlForInstallablePaymentApps() = 0;
......
...@@ -137,6 +137,12 @@ PaymentRequestState::GetMethodData() const { ...@@ -137,6 +137,12 @@ PaymentRequestState::GetMethodData() const {
return GetSpec()->method_data(); return GetSpec()->method_data();
} }
std::unique_ptr<autofill::InternalAuthenticator>
PaymentRequestState::CreateInternalAuthenticator() const {
return GetPaymentRequestDelegate()->CreateInternalAuthenticator(
initiator_render_frame_host_);
}
scoped_refptr<PaymentManifestWebDataService> scoped_refptr<PaymentManifestWebDataService>
PaymentRequestState::GetPaymentManifestWebDataService() const { PaymentRequestState::GetPaymentManifestWebDataService() const {
return GetPaymentRequestDelegate()->GetPaymentManifestWebDataService(); return GetPaymentRequestDelegate()->GetPaymentManifestWebDataService();
......
...@@ -139,6 +139,8 @@ class PaymentRequestState : public PaymentAppFactory::Delegate, ...@@ -139,6 +139,8 @@ class PaymentRequestState : public PaymentAppFactory::Delegate,
content::RenderFrameHost* GetInitiatorRenderFrameHost() const override; content::RenderFrameHost* GetInitiatorRenderFrameHost() const override;
const std::vector<mojom::PaymentMethodDataPtr>& GetMethodData() const std::vector<mojom::PaymentMethodDataPtr>& GetMethodData()
const override; const override;
std::unique_ptr<autofill::InternalAuthenticator> CreateInternalAuthenticator()
const override;
scoped_refptr<PaymentManifestWebDataService> scoped_refptr<PaymentManifestWebDataService>
GetPaymentManifestWebDataService() const override; GetPaymentManifestWebDataService() const override;
const std::vector<autofill::AutofillProfile*>& GetBillingProfiles() override; const std::vector<autofill::AutofillProfile*>& GetBillingProfiles() override;
......
...@@ -6,32 +6,83 @@ ...@@ -6,32 +6,83 @@
#include <utility> #include <utility>
#include "base/check.h"
#include "base/containers/flat_tree.h"
#include "base/notreached.h" #include "base/notreached.h"
#include "base/strings/strcat.h"
#include "base/time/time.h"
#include "components/autofill/core/browser/payments/internal_authenticator.h"
#include "components/payments/core/method_strings.h" #include "components/payments/core/method_strings.h"
#include "components/payments/core/payer_data.h" #include "components/payments/core/payer_data.h"
#include "device/fido/fido_transport_protocol.h"
#include "device/fido/fido_types.h"
#include "device/fido/public_key_credential_descriptor.h"
#include "url/url_constants.h"
namespace payments { namespace payments {
namespace {
static constexpr int kDefaultTimeoutMinutes = 3;
} // namespace
SecurePaymentConfirmationApp::SecurePaymentConfirmationApp( SecurePaymentConfirmationApp::SecurePaymentConfirmationApp(
const std::string& effective_relying_party_identity,
std::unique_ptr<SkBitmap> icon, std::unique_ptr<SkBitmap> icon,
const base::string16& label, const base::string16& label,
std::vector<std::unique_ptr<std::vector<uint8_t>>> credential_ids,
const url::Origin& merchant_origin, const url::Origin& merchant_origin,
const mojom::PaymentCurrencyAmountPtr& total, const mojom::PaymentCurrencyAmountPtr& total,
const mojom::SecurePaymentConfirmationRequestPtr& request) mojom::SecurePaymentConfirmationRequestPtr request,
std::unique_ptr<autofill::InternalAuthenticator> authenticator)
: PaymentApp(/*icon_resource_id=*/0, PaymentApp::Type::INTERNAL), : PaymentApp(/*icon_resource_id=*/0, PaymentApp::Type::INTERNAL),
effective_relying_party_identity_(effective_relying_party_identity),
icon_(std::move(icon)), icon_(std::move(icon)),
label_(label), label_(label),
credential_ids_(std::move(credential_ids)),
merchant_origin_(merchant_origin), merchant_origin_(merchant_origin),
total_(total.Clone()), total_(total.Clone()),
request_(request.Clone()) { request_(std::move(request)),
authenticator_(std::move(authenticator)) {
DCHECK(!credential_ids_.empty());
DCHECK(credential_ids_.front());
DCHECK(!credential_ids_.front()->empty());
app_method_names_.insert(methods::kSecurePaymentConfirmation); app_method_names_.insert(methods::kSecurePaymentConfirmation);
} }
SecurePaymentConfirmationApp::~SecurePaymentConfirmationApp() = default; SecurePaymentConfirmationApp::~SecurePaymentConfirmationApp() = default;
void SecurePaymentConfirmationApp::InvokePaymentApp(Delegate* delegate) { void SecurePaymentConfirmationApp::InvokePaymentApp(Delegate* delegate) {
std::vector<device::PublicKeyCredentialDescriptor> credentials;
for (const auto& credential_id : credential_ids_) {
credentials.emplace_back(device::CredentialType::kPublicKey, *credential_id,
base::flat_set<device::FidoTransportProtocol>{
device::FidoTransportProtocol::kInternal});
}
auto options = blink::mojom::PublicKeyCredentialRequestOptions::New();
options->relying_party_id = effective_relying_party_identity_;
options->timeout = request_->timeout.has_value()
? request_->timeout.value()
: base::TimeDelta::FromMinutes(kDefaultTimeoutMinutes);
options->user_verification = device::UserVerificationRequirement::kRequired;
options->allow_credentials = std::move(credentials);
// TODO(https://crbug.com/1110324): Combine |merchant_origin_|, |total_|, and // TODO(https://crbug.com/1110324): Combine |merchant_origin_|, |total_|, and
// |request_| into a challenge to invoke WebAuthn. // |request_->network_data| into a challenge to invoke the authenticator.
options->challenge = request_->network_data;
// We are nullifying the security check by design, and the origin that created
// the credential isn't saved anywhere.
authenticator_->SetEffectiveOrigin(url::Origin::Create(
GURL(base::StrCat({url::kHttpsScheme, url::kStandardSchemeSeparator,
effective_relying_party_identity_}))));
authenticator_->GetAssertion(
std::move(options),
base::BindOnce(&SecurePaymentConfirmationApp::OnGetAssertion,
weak_ptr_factory_.GetWeakPtr(), delegate));
} }
bool SecurePaymentConfirmationApp::IsCompleteForPayment() const { bool SecurePaymentConfirmationApp::IsCompleteForPayment() const {
...@@ -127,7 +178,26 @@ void SecurePaymentConfirmationApp::OnPaymentDetailsNotUpdated() { ...@@ -127,7 +178,26 @@ void SecurePaymentConfirmationApp::OnPaymentDetailsNotUpdated() {
void SecurePaymentConfirmationApp::AbortPaymentApp( void SecurePaymentConfirmationApp::AbortPaymentApp(
base::OnceCallback<void(bool)> abort_callback) { base::OnceCallback<void(bool)> abort_callback) {
authenticator_->Cancel();
std::move(abort_callback).Run(/*abort_success=*/true); std::move(abort_callback).Run(/*abort_success=*/true);
} }
void SecurePaymentConfirmationApp::OnGetAssertion(
Delegate* delegate,
blink::mojom::AuthenticatorStatus status,
blink::mojom::GetAssertionAuthenticatorResponsePtr response) {
if (status != blink::mojom::AuthenticatorStatus::SUCCESS) {
delegate->OnInstrumentDetailsError("Authentication failure.");
return;
}
// TODO(https://crbug.com/1110324): Serialize response into a JSON string.
// Browser will pass this string over Mojo IPC into Blink, which will parse it
// into a JavaScript object for the merchant.
std::string json_serialized_response = "{\"status\": \"success\"}";
delegate->OnInstrumentDetailsReady(methods::kSecurePaymentConfirmation,
json_serialized_response, PayerData());
}
} // namespace payments } // namespace payments
...@@ -16,20 +16,28 @@ ...@@ -16,20 +16,28 @@
#include "base/strings/string16.h" #include "base/strings/string16.h"
#include "components/payments/content/secure_payment_confirmation_controller.h" #include "components/payments/content/secure_payment_confirmation_controller.h"
#include "third_party/blink/public/mojom/payments/payment_request.mojom.h" #include "third_party/blink/public/mojom/payments/payment_request.mojom.h"
#include "third_party/blink/public/mojom/webauthn/authenticator.mojom.h"
#include "url/origin.h" #include "url/origin.h"
class SkBitmap; class SkBitmap;
namespace autofill {
class InternalAuthenticator;
} // namespace autofill
namespace payments { namespace payments {
class SecurePaymentConfirmationApp : public PaymentApp { class SecurePaymentConfirmationApp : public PaymentApp {
public: public:
SecurePaymentConfirmationApp( SecurePaymentConfirmationApp(
const std::string& effective_relying_party_identity,
std::unique_ptr<SkBitmap> icon, std::unique_ptr<SkBitmap> icon,
const base::string16& label, const base::string16& label,
std::vector<std::unique_ptr<std::vector<uint8_t>>> credential_ids,
const url::Origin& merchant_origin, const url::Origin& merchant_origin,
const mojom::PaymentCurrencyAmountPtr& total, const mojom::PaymentCurrencyAmountPtr& total,
const mojom::SecurePaymentConfirmationRequestPtr& request); mojom::SecurePaymentConfirmationRequestPtr request,
std::unique_ptr<autofill::InternalAuthenticator> authenticator);
~SecurePaymentConfirmationApp() override; ~SecurePaymentConfirmationApp() override;
SecurePaymentConfirmationApp(const SecurePaymentConfirmationApp& other) = SecurePaymentConfirmationApp(const SecurePaymentConfirmationApp& other) =
...@@ -66,11 +74,19 @@ class SecurePaymentConfirmationApp : public PaymentApp { ...@@ -66,11 +74,19 @@ class SecurePaymentConfirmationApp : public PaymentApp {
void AbortPaymentApp(base::OnceCallback<void(bool)> abort_callback) override; void AbortPaymentApp(base::OnceCallback<void(bool)> abort_callback) override;
private: private:
void OnGetAssertion(
Delegate* delegate,
blink::mojom::AuthenticatorStatus status,
blink::mojom::GetAssertionAuthenticatorResponsePtr response);
const std::string effective_relying_party_identity_;
const std::unique_ptr<SkBitmap> icon_; const std::unique_ptr<SkBitmap> icon_;
const base::string16 label_; const base::string16 label_;
const std::vector<std::unique_ptr<std::vector<uint8_t>>> credential_ids_;
const url::Origin merchant_origin_; const url::Origin merchant_origin_;
const mojom::PaymentCurrencyAmountPtr total_; const mojom::PaymentCurrencyAmountPtr total_;
const mojom::SecurePaymentConfirmationRequestPtr request_; const mojom::SecurePaymentConfirmationRequestPtr request_;
const std::unique_ptr<autofill::InternalAuthenticator> authenticator_;
base::WeakPtrFactory<SecurePaymentConfirmationApp> weak_ptr_factory_{this}; base::WeakPtrFactory<SecurePaymentConfirmationApp> weak_ptr_factory_{this};
}; };
......
...@@ -4,17 +4,92 @@ ...@@ -4,17 +4,92 @@
#include "components/payments/content/secure_payment_confirmation_app_factory.h" #include "components/payments/content/secure_payment_confirmation_app_factory.h"
#include <stdint.h>
#include <memory> #include <memory>
#include <string>
#include <utility>
#include <vector>
#include "base/stl_util.h" #include "base/stl_util.h"
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
#include "components/autofill/core/browser/payments/internal_authenticator.h"
#include "components/payments/content/payment_manifest_web_data_service.h"
#include "components/payments/content/payment_request_spec.h" #include "components/payments/content/payment_request_spec.h"
#include "components/payments/content/secure_payment_confirmation_app.h" #include "components/payments/content/secure_payment_confirmation_app.h"
#include "components/payments/core/method_strings.h" #include "components/payments/core/method_strings.h"
#include "components/payments/core/native_error_strings.h"
#include "third_party/blink/public/mojom/payments/payment_request.mojom.h" #include "third_party/blink/public/mojom/payments/payment_request.mojom.h"
#include "url/origin.h" #include "url/origin.h"
namespace payments { namespace payments {
namespace {
// Arbitrarily chosen limit of 1 hour. Keep in sync with
// secure_payment_confirmation_helper.cc.
constexpr int64_t kMaxTimeoutInMilliseconds = 1000 * 60 * 60;
bool IsValid(const mojom::SecurePaymentConfirmationRequestPtr& request,
std::string* error_message) {
// |request| can be null when the feature is disabled in Blink.
if (!request)
return false;
if (request->instrument_id.empty()) {
*error_message = errors::kInstrumentIdRequired;
return false;
}
if (request->timeout.has_value() &&
request->timeout.value().InMilliseconds() > kMaxTimeoutInMilliseconds) {
*error_message = errors::kTimeoutTooLong;
return false;
}
return true;
}
void OnIsUserVerifyingPlatformAuthenticatorAvailable(
base::WeakPtr<PaymentAppFactory::Delegate> delegate,
mojom::SecurePaymentConfirmationRequestPtr request,
std::unique_ptr<autofill::InternalAuthenticator> authenticator,
bool is_available) {
if (!delegate)
return;
if (!is_available) {
delegate->OnDoneCreatingPaymentApps();
return;
}
scoped_refptr<payments::PaymentManifestWebDataService> web_data_service =
delegate->GetPaymentManifestWebDataService();
if (!web_data_service) {
delegate->OnDoneCreatingPaymentApps();
return;
}
// TODO(https://crbug.com/1110324): Check |web_data_service| for whether
// |request->instrument_id| has any credentials on this device. If so,
// retrieve the instrument information from |web_data_service| and use these
// values to create a SecurePaymentConfirmationApp. For now, use stubs.
std::string effective_relying_party_identity = "rp.example";
std::unique_ptr<SkBitmap> icon;
base::string16 label = base::ASCIIToUTF16("Stub label");
std::vector<std::unique_ptr<std::vector<uint8_t>>> credential_ids;
credential_ids.emplace_back(std::make_unique<std::vector<uint8_t>>());
credential_ids.back()->push_back(0);
delegate->OnPaymentAppCreated(std::make_unique<SecurePaymentConfirmationApp>(
effective_relying_party_identity, std::move(icon), label,
std::move(credential_ids),
/*merchant_origin=*/url::Origin::Create(delegate->GetTopOrigin()),
/*total=*/delegate->GetSpec()->details().total->amount,
std::move(request), std::move(authenticator)));
delegate->OnDoneCreatingPaymentApps();
}
} // namespace
SecurePaymentConfirmationAppFactory::SecurePaymentConfirmationAppFactory() SecurePaymentConfirmationAppFactory::SecurePaymentConfirmationAppFactory()
: PaymentAppFactory(PaymentApp::Type::INTERNAL) {} : PaymentAppFactory(PaymentApp::Type::INTERNAL) {}
...@@ -32,23 +107,24 @@ void SecurePaymentConfirmationAppFactory::Create( ...@@ -32,23 +107,24 @@ void SecurePaymentConfirmationAppFactory::Create(
} }
for (const mojom::PaymentMethodDataPtr& method_data : spec->method_data()) { for (const mojom::PaymentMethodDataPtr& method_data : spec->method_data()) {
if (method_data->supported_method == methods::kSecurePaymentConfirmation && if (method_data->supported_method == methods::kSecurePaymentConfirmation) {
method_data->secure_payment_confirmation) { std::string error_message;
// TODO(https://crbug.com/1110324): Check storage for whether if (!IsValid(method_data->secure_payment_confirmation, &error_message)) {
// |method_data->secure_payment_confirmation->instrument_id| has any if (!error_message.empty())
// credentials on this device. If so, retrieve the instrument icon and delegate->OnPaymentAppCreationError(error_message);
// label from storage and use these values to create a delegate->OnDoneCreatingPaymentApps();
// SecurePaymentConfirmationApp. return;
}
// A stub payment app that contains the secure payment confirmation
// request, but does not yet invoke WebAuthn at this time. std::unique_ptr<autofill::InternalAuthenticator> authenticator =
delegate->OnPaymentAppCreated( delegate->CreateInternalAuthenticator();
std::make_unique<SecurePaymentConfirmationApp>(
/*icon=*/nullptr, /*label=*/base::ASCIIToUTF16("Stub label"), authenticator->IsUserVerifyingPlatformAuthenticatorAvailable(
/*merchant_origin=*/url::Origin::Create(delegate->GetTopOrigin()), base::BindOnce(&OnIsUserVerifyingPlatformAuthenticatorAvailable,
/*total=*/spec->details().total->amount, delegate,
/*request=*/method_data->secure_payment_confirmation)); method_data->secure_payment_confirmation.Clone(),
break; std::move(authenticator)));
return;
} }
} }
......
...@@ -29,6 +29,12 @@ SecurePaymentConfirmationPaymentRequestDelegate:: ...@@ -29,6 +29,12 @@ SecurePaymentConfirmationPaymentRequestDelegate::
SecurePaymentConfirmationPaymentRequestDelegate:: SecurePaymentConfirmationPaymentRequestDelegate::
~SecurePaymentConfirmationPaymentRequestDelegate() = default; ~SecurePaymentConfirmationPaymentRequestDelegate() = default;
std::unique_ptr<autofill::InternalAuthenticator>
SecurePaymentConfirmationPaymentRequestDelegate::CreateInternalAuthenticator(
content::RenderFrameHost* rfh) const {
return delegate_->CreateInternalAuthenticator(rfh);
}
scoped_refptr<PaymentManifestWebDataService> scoped_refptr<PaymentManifestWebDataService>
SecurePaymentConfirmationPaymentRequestDelegate:: SecurePaymentConfirmationPaymentRequestDelegate::
GetPaymentManifestWebDataService() const { GetPaymentManifestWebDataService() const {
......
...@@ -29,6 +29,8 @@ class SecurePaymentConfirmationPaymentRequestDelegate ...@@ -29,6 +29,8 @@ class SecurePaymentConfirmationPaymentRequestDelegate
const SecurePaymentConfirmationPaymentRequestDelegate& other) = delete; const SecurePaymentConfirmationPaymentRequestDelegate& other) = delete;
// ContentPaymentRequestDelegate implementation: // ContentPaymentRequestDelegate implementation:
std::unique_ptr<autofill::InternalAuthenticator> CreateInternalAuthenticator(
content::RenderFrameHost* rfh) const override;
scoped_refptr<PaymentManifestWebDataService> scoped_refptr<PaymentManifestWebDataService>
GetPaymentManifestWebDataService() const override; GetPaymentManifestWebDataService() const override;
PaymentRequestDisplayManager* GetDisplayManager() override; PaymentRequestDisplayManager* GetDisplayManager() override;
......
...@@ -15,6 +15,12 @@ TestContentPaymentRequestDelegate::TestContentPaymentRequestDelegate( ...@@ -15,6 +15,12 @@ TestContentPaymentRequestDelegate::TestContentPaymentRequestDelegate(
TestContentPaymentRequestDelegate::~TestContentPaymentRequestDelegate() {} TestContentPaymentRequestDelegate::~TestContentPaymentRequestDelegate() {}
std::unique_ptr<autofill::InternalAuthenticator>
TestContentPaymentRequestDelegate::CreateInternalAuthenticator(
content::RenderFrameHost* rfh) const {
return nullptr;
}
scoped_refptr<PaymentManifestWebDataService> scoped_refptr<PaymentManifestWebDataService>
TestContentPaymentRequestDelegate::GetPaymentManifestWebDataService() const { TestContentPaymentRequestDelegate::GetPaymentManifestWebDataService() const {
return nullptr; return nullptr;
......
...@@ -22,6 +22,8 @@ class TestContentPaymentRequestDelegate : public ContentPaymentRequestDelegate { ...@@ -22,6 +22,8 @@ class TestContentPaymentRequestDelegate : public ContentPaymentRequestDelegate {
~TestContentPaymentRequestDelegate() override; ~TestContentPaymentRequestDelegate() override;
// ContentPaymentRequestDelegate: // ContentPaymentRequestDelegate:
std::unique_ptr<autofill::InternalAuthenticator> CreateInternalAuthenticator(
content::RenderFrameHost* rfh) const override;
scoped_refptr<PaymentManifestWebDataService> scoped_refptr<PaymentManifestWebDataService>
GetPaymentManifestWebDataService() const override; GetPaymentManifestWebDataService() const override;
PaymentRequestDisplayManager* GetDisplayManager() override; PaymentRequestDisplayManager* GetDisplayManager() override;
......
...@@ -210,5 +210,13 @@ const char kMoreThanOneService[] = ...@@ -210,5 +210,13 @@ const char kMoreThanOneService[] =
"Found more than one IS_READY_TO_PAY service, but at most one service is " "Found more than one IS_READY_TO_PAY service, but at most one service is "
"supported."; "supported.";
const char kInstrumentIdRequired[] =
"The \"secure-payment-confirmation\" method requires a non-empty "
"\"instrumentId\" field.";
const char kTimeoutTooLong[] =
"The \"secure-payment-confirmation\" method requires at most 1 hour "
"\"timeout\" field.";
} // namespace errors } // namespace errors
} // namespace payments } // namespace payments
...@@ -237,6 +237,14 @@ extern const char kUserClosedPaymentApp[]; ...@@ -237,6 +237,14 @@ extern const char kUserClosedPaymentApp[];
// Used when an Android app declares more than one IS_READY_TO_PAY service. // Used when an Android app declares more than one IS_READY_TO_PAY service.
extern const char kMoreThanOneService[]; extern const char kMoreThanOneService[];
// Used when an empty instrument ID is specified for the
// "secure-payment-confirmation" method.
extern const char kInstrumentIdRequired[];
// Used when the timeout specified for the "secure-payment-confirmation" method
// is too long.
extern const char kTimeoutTooLong[];
} // namespace errors } // namespace errors
} // namespace payments } // namespace payments
......
...@@ -15,7 +15,8 @@ ...@@ -15,7 +15,8 @@
namespace blink { namespace blink {
namespace { namespace {
// Arbitrarily chosen limit of 1 hour. // Arbitrarily chosen limit of 1 hour. Keep in sync with
// secure_payment_confirmation_app_factory.cc.
constexpr uint32_t kMaxTimeoutInMilliseconds = 1000 * 60 * 60; constexpr uint32_t kMaxTimeoutInMilliseconds = 1000 * 60 * 60;
} // namespace } // namespace
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment