blob: 0c1868a98f67b878f332567adb75282beb1eec06 [file] [log] [blame]
// Copyright 2020 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/payments/content/secure_payment_confirmation_app.h"
#include <utility>
#include "base/base64.h"
#include "base/base64url.h"
#include "base/check.h"
#include "base/containers/flat_tree.h"
#include "base/feature_list.h"
#include "base/json/json_writer.h"
#include "base/metrics/histogram_functions.h"
#include "base/notreached.h"
#include "base/time/time.h"
#include "base/values.h"
#include "components/payments/content/payment_request_spec.h"
#include "components/payments/core/error_strings.h"
#include "components/payments/core/method_strings.h"
#include "components/payments/core/payer_data.h"
#include "components/webauthn/core/browser/internal_authenticator.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/content_features.h"
#include "crypto/sha2.h"
#include "device/fido/fido_transport_protocol.h"
#include "device/fido/fido_types.h"
#include "device/fido/public_key_credential_descriptor.h"
#include "third_party/blink/public/mojom/webauthn/authenticator.mojom.h"
#include "url/url_constants.h"
namespace payments {
namespace {
static constexpr int kDefaultTimeoutMinutes = 3;
// Records UMA metric for the system prompt result.
void RecordSystemPromptResult(
const SecurePaymentConfirmationSystemPromptResult result) {
base::UmaHistogramEnumeration(
"PaymentRequest.SecurePaymentConfirmation.Funnel.SystemPromptResult",
result);
}
} // namespace
SecurePaymentConfirmationApp::SecurePaymentConfirmationApp(
content::WebContents* web_contents_to_observe,
const std::string& effective_relying_party_identity,
std::unique_ptr<SkBitmap> icon,
const std::u16string& label,
std::vector<uint8_t> credential_id,
const url::Origin& merchant_origin,
base::WeakPtr<PaymentRequestSpec> spec,
mojom::SecurePaymentConfirmationRequestPtr request,
std::unique_ptr<webauthn::InternalAuthenticator> authenticator)
: PaymentApp(/*icon_resource_id=*/0, PaymentApp::Type::INTERNAL),
content::WebContentsObserver(web_contents_to_observe),
authenticator_frame_routing_id_(
authenticator->GetRenderFrameHost()->GetGlobalId()),
effective_relying_party_identity_(effective_relying_party_identity),
icon_(std::move(icon)),
label_(label),
credential_id_(std::move(credential_id)),
merchant_origin_(merchant_origin),
spec_(spec),
request_(std::move(request)),
authenticator_(std::move(authenticator)) {
DCHECK(!credential_id_.empty());
app_method_names_.insert(methods::kSecurePaymentConfirmation);
}
SecurePaymentConfirmationApp::~SecurePaymentConfirmationApp() = default;
void SecurePaymentConfirmationApp::InvokePaymentApp(
base::WeakPtr<Delegate> delegate) {
if (!authenticator_ || !spec_)
return;
DCHECK(spec_->IsInitialized());
auto options = blink::mojom::PublicKeyCredentialRequestOptions::New();
options->relying_party_id = effective_relying_party_identity_;
options->timeout = request_->timeout.has_value()
? request_->timeout.value()
: base::Minutes(kDefaultTimeoutMinutes);
options->user_verification = device::UserVerificationRequirement::kRequired;
std::vector<device::PublicKeyCredentialDescriptor> credentials;
if (base::FeatureList::IsEnabled(features::kSecurePaymentConfirmationDebug)) {
options->user_verification =
device::UserVerificationRequirement::kPreferred;
// The `device::PublicKeyCredentialDescriptor` constructor with 2 parameters
// enables authentication through all protocols.
credentials.emplace_back(device::CredentialType::kPublicKey,
credential_id_);
} else {
// Enable authentication only through internal authenticators by default.
credentials.emplace_back(device::CredentialType::kPublicKey, credential_id_,
base::flat_set<device::FidoTransportProtocol>{
device::FidoTransportProtocol::kInternal});
}
options->allow_credentials = std::move(credentials);
options->challenge = request_->challenge;
authenticator_->SetPaymentOptions(blink::mojom::PaymentOptions::New(
spec_->GetTotal(/*selected_app=*/this)->amount.Clone(),
request_->instrument.Clone(), request_->payee_name,
request_->payee_origin));
authenticator_->GetAssertion(
std::move(options),
base::BindOnce(&SecurePaymentConfirmationApp::OnGetAssertion,
weak_ptr_factory_.GetWeakPtr(), delegate));
}
bool SecurePaymentConfirmationApp::IsCompleteForPayment() const {
return true;
}
uint32_t SecurePaymentConfirmationApp::GetCompletenessScore() const {
// This value is used for sorting multiple apps, but this app always appears
// on its own.
return 0;
}
bool SecurePaymentConfirmationApp::CanPreselect() const {
return true;
}
std::u16string SecurePaymentConfirmationApp::GetMissingInfoLabel() const {
NOTREACHED();
return std::u16string();
}
bool SecurePaymentConfirmationApp::HasEnrolledInstrument() const {
// If there's no platform authenticator, then the factory should not create
// this app. Therefore, this function can always return true.
return true;
}
void SecurePaymentConfirmationApp::RecordUse() {
NOTIMPLEMENTED();
}
bool SecurePaymentConfirmationApp::NeedsInstallation() const {
return false;
}
std::string SecurePaymentConfirmationApp::GetId() const {
return base::Base64Encode(credential_id_);
}
std::u16string SecurePaymentConfirmationApp::GetLabel() const {
return label_;
}
std::u16string SecurePaymentConfirmationApp::GetSublabel() const {
return std::u16string();
}
const SkBitmap* SecurePaymentConfirmationApp::icon_bitmap() const {
return icon_.get();
}
bool SecurePaymentConfirmationApp::IsValidForModifier(
const std::string& method,
bool supported_networks_specified,
const std::set<std::string>& supported_networks) const {
bool is_valid = false;
IsValidForPaymentMethodIdentifier(method, &is_valid);
return is_valid;
}
base::WeakPtr<PaymentApp> SecurePaymentConfirmationApp::AsWeakPtr() {
return weak_ptr_factory_.GetWeakPtr();
}
bool SecurePaymentConfirmationApp::HandlesShippingAddress() const {
return false;
}
bool SecurePaymentConfirmationApp::HandlesPayerName() const {
return false;
}
bool SecurePaymentConfirmationApp::HandlesPayerEmail() const {
return false;
}
bool SecurePaymentConfirmationApp::HandlesPayerPhone() const {
return false;
}
bool SecurePaymentConfirmationApp::IsWaitingForPaymentDetailsUpdate() const {
return false;
}
void SecurePaymentConfirmationApp::UpdateWith(
mojom::PaymentRequestDetailsUpdatePtr details_update) {
NOTREACHED();
}
void SecurePaymentConfirmationApp::OnPaymentDetailsNotUpdated() {
NOTREACHED();
}
void SecurePaymentConfirmationApp::AbortPaymentApp(
base::OnceCallback<void(bool)> abort_callback) {
std::move(abort_callback).Run(/*abort_success=*/false);
}
mojom::PaymentResponsePtr
SecurePaymentConfirmationApp::SetAppSpecificResponseFields(
mojom::PaymentResponsePtr response) const {
response->secure_payment_confirmation =
mojom::SecurePaymentConfirmationResponse::New(
response_->info.Clone(), response_->signature,
response_->authenticator_attachment, response_->user_handle);
return response;
}
void SecurePaymentConfirmationApp::RenderFrameDeleted(
content::RenderFrameHost* render_frame_host) {
if (content::RenderFrameHost::FromID(authenticator_frame_routing_id_) ==
render_frame_host) {
// The authenticator requires to be deleted before the render frame.
authenticator_.reset();
}
}
void SecurePaymentConfirmationApp::OnGetAssertion(
base::WeakPtr<Delegate> delegate,
blink::mojom::AuthenticatorStatus status,
blink::mojom::GetAssertionAuthenticatorResponsePtr response,
blink::mojom::WebAuthnDOMExceptionDetailsPtr dom_exception_details) {
if (!delegate)
return;
if (status != blink::mojom::AuthenticatorStatus::SUCCESS || !response) {
delegate->OnInstrumentDetailsError(
errors::kWebAuthnOperationTimedOutOrNotAllowed);
RecordSystemPromptResult(
SecurePaymentConfirmationSystemPromptResult::kCanceled);
return;
}
RecordSystemPromptResult(
SecurePaymentConfirmationSystemPromptResult::kAccepted);
response_ = std::move(response);
delegate->OnInstrumentDetailsReady(methods::kSecurePaymentConfirmation, "{}",
PayerData());
}
} // namespace payments