Commit 1f4b9be7 authored by Rouslan Solomakhin's avatar Rouslan Solomakhin Committed by Commit Bot

Reland "[Web Payment] Secure payment confirmation Blink parameters."

This is a reland of b772a8f1
Original patch: https://crrev.com/c/2341163
Revert: https://crrev.com/c/2348532

Reason for revert: New WPT test revealed a memory issue in
//third_party/blink/renderer/modules/payments/payments_validators.cc
when serializing data larger than than 1MB into JSON.

Reason for reland: Split out the large data serialization tests into a
separate patches for fixing the JSON serialization issue separately:
 1) https://crrev.com/c/2348411
 2) https://crrev.com/c/2348540
 3) https://crrev.com/c/2346973

Original change's description:
> [Web Payment] Secure payment confirmation Blink parameters.
>
> Before this patch, requesting "secure-payment-confirmation" method in
> PaymentRequest API would not validate the parameters.
>
> This patch adds the "secure-payment-confirmation" parameters to
> PaymentRequest API and validates them, if the
> "SecurePaymentConfirmation" feature is enabled.
>
> After this patch, PaymentRequest API validates the secure payment
> confirmation parameters.
>
> Explainer: https://github.com/rsolomakhin/secure-payment-confirmation
> Feature status: https://chromestatus.com/feature/5702310124584960
> Intent to prototype:
> https://groups.google.com/a/chromium.org/g/blink-dev/c/myUR5gyd5Js/m/iELL67NQAgAJ
>
> Bug: 1110324
> Change-Id: I7a4b6f9da1b3e1d7604bcd9e4733dc789f77ec4b
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2341163
> Reviewed-by: Nick Burris <nburris@chromium.org>
> Reviewed-by: Kouhei Ueno <kouhei@chromium.org>
> Auto-Submit: Rouslan Solomakhin <rouslan@chromium.org>
> Commit-Queue: Rouslan Solomakhin <rouslan@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#796792}

TBR=ellyjones, nburris, kouhei

Bug: 1110324, 1115091
Change-Id: I05ebdbfe234d3bee8ceb2016dafc5679a9a28ef9
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2348410
Commit-Queue: Rouslan Solomakhin <rouslan@chromium.org>
Reviewed-by: default avatarRouslan Solomakhin <rouslan@chromium.org>
Reviewed-by: default avatarNick Burris <nburris@chromium.org>
Cr-Commit-Position: refs/heads/master@{#796954}
parent 57dbb758
......@@ -418,6 +418,7 @@ static_idl_files_in_modules = get_path_info(
"//third_party/blink/renderer/modules/payments/address_init.idl",
"//third_party/blink/renderer/modules/payments/android_pay_method_data.idl",
"//third_party/blink/renderer/modules/payments/basic_card_request.idl",
"//third_party/blink/renderer/modules/payments/secure_payment_confirmation_request.idl",
"//third_party/blink/renderer/modules/payments/can_make_payment_event.idl",
"//third_party/blink/renderer/modules/payments/can_make_payment_event_init.idl",
"//third_party/blink/renderer/modules/payments/can_make_payment_response.idl",
......
......@@ -51,6 +51,8 @@ blink_modules_sources("payments") {
"payment_state_resolver.h",
"payments_validators.cc",
"payments_validators.h",
"secure_payment_confirmation_helper.cc",
"secure_payment_confirmation_helper.h",
"update_payment_details_function.cc",
"update_payment_details_function.h",
]
......
......@@ -21,6 +21,7 @@ modules_dictionary_idl_files = [
"address_init.idl",
"android_pay_method_data.idl",
"basic_card_request.idl",
"secure_payment_confirmation_request.idl",
"can_make_payment_event_init.idl",
"can_make_payment_response.idl",
"image_object.idl",
......
......@@ -51,6 +51,7 @@
#include "third_party/blink/renderer/modules/payments/payment_request_update_event.h"
#include "third_party/blink/renderer/modules/payments/payment_response.h"
#include "third_party/blink/renderer/modules/payments/payments_validators.h"
#include "third_party/blink/renderer/modules/payments/secure_payment_confirmation_helper.h"
#include "third_party/blink/renderer/modules/payments/update_payment_details_function.h"
#include "third_party/blink/renderer/platform/bindings/exception_state.h"
#include "third_party/blink/renderer/platform/bindings/script_state.h"
......@@ -411,14 +412,14 @@ void SetAndroidPayMethodData(v8::Isolate* isolate,
output->api_version = android_pay->apiVersion();
}
void StringifyAndParseMethodSpecificData(v8::Isolate* isolate,
void StringifyAndParseMethodSpecificData(ExecutionContext& execution_context,
const String& supported_method,
const ScriptValue& input,
PaymentMethodDataPtr& output,
ExceptionState& exception_state) {
PaymentsValidators::ValidateAndStringifyObject(
isolate, "Payment method data", input, output->stringified_data,
exception_state);
execution_context.GetIsolate(), "Payment method data", input,
output->stringified_data, exception_state);
if (exception_state.HadException())
return;
......@@ -427,15 +428,21 @@ void StringifyAndParseMethodSpecificData(v8::Isolate* isolate,
// data asynchronously. Do not throw exceptions here.
if (supported_method == kGooglePayMethod ||
supported_method == kAndroidPayMethod) {
SetAndroidPayMethodData(isolate, input, output, exception_state);
SetAndroidPayMethodData(execution_context.GetIsolate(), input, output,
exception_state);
if (exception_state.HadException())
exception_state.ClearException();
}
// Parse method data to avoid parsing JSON in the browser.
if (supported_method == "basic-card") {
// Parses basic-card data to avoid parsing JSON in the browser.
BasicCardHelper::ParseBasiccardData(input, output->supported_networks,
exception_state);
} else if (supported_method == "secure-payment-confirmation" &&
RuntimeEnabledFeatures::SecurePaymentConfirmationEnabled(
&execution_context)) {
SecurePaymentConfirmationHelper::ParseSecurePaymentConfirmationData(
input, exception_state);
}
}
......@@ -480,8 +487,8 @@ void ValidateAndConvertPaymentDetailsModifiers(
if (modifier->hasData() && !modifier->data().IsEmpty()) {
StringifyAndParseMethodSpecificData(
execution_context.GetIsolate(), modifier->supportedMethod(),
modifier->data(), output.back()->method_data, exception_state);
execution_context, modifier->supportedMethod(), modifier->data(),
output.back()->method_data, exception_state);
} else {
output.back()->method_data->stringified_data = "";
}
......@@ -677,9 +684,8 @@ void ValidateAndConvertPaymentMethodData(
if (payment_method_data->hasData() &&
!payment_method_data->data().IsEmpty()) {
StringifyAndParseMethodSpecificData(
execution_context.GetIsolate(),
payment_method_data->supportedMethod(), payment_method_data->data(),
output.back(), exception_state);
execution_context, payment_method_data->supportedMethod(),
payment_method_data->data(), output.back(), exception_state);
if (exception_state.HadException())
continue;
......@@ -1199,8 +1205,9 @@ PaymentRequest::PaymentRequest(
mojom::blink::ConsoleMessageSource::kJavaScript,
mojom::blink::ConsoleMessageLevel::kError,
"Payment method \"" + data->supported_method +
"\" cannot be used with \"requestShipping\", \"requestPayerName\", "
"\"requestPayerEmail\", or \"requestPayerPhone\"."));
"\" cannot be used with \"requestShipping\", "
"\"requestPayerName\", "
"\"requestPayerEmail\", or \"requestPayerPhone\"."));
}
}
......
// 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 "third_party/blink/renderer/modules/payments/secure_payment_confirmation_helper.h"
#include <stdint.h>
#include "base/logging.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_secure_payment_confirmation_request.h"
#include "third_party/blink/renderer/platform/bindings/exception_state.h"
namespace blink {
namespace {
// Arbitrarily chosen limit of 1 hour.
constexpr uint32_t kMaxTimeoutInMilliseconds = 1000 * 60 * 60;
} // namespace
// static
void SecurePaymentConfirmationHelper::ParseSecurePaymentConfirmationData(
const ScriptValue& input,
ExceptionState& exception_state) {
DCHECK(!input.IsEmpty());
SecurePaymentConfirmationRequest* request =
NativeValueTraits<SecurePaymentConfirmationRequest>::NativeValue(
input.GetIsolate(), input.V8Value(), exception_state);
if (exception_state.HadException())
return;
if (request->instrumentId().IsEmpty()) {
exception_state.ThrowRangeError(
"The \"secure-payment-confirmation\" method requires a non-empty "
"\"instrumentId\" field.");
return;
}
if (request->hasTimeout() && request->timeout() > kMaxTimeoutInMilliseconds) {
exception_state.ThrowRangeError(
"The \"secure-payment-confirmation\" method requires at most 1 hour "
"\"timeout\" field.");
return;
}
}
} // namespace blink
// 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.
#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_PAYMENTS_SECURE_PAYMENT_CONFIRMATION_HELPER_H_
#define THIRD_PARTY_BLINK_RENDERER_MODULES_PAYMENTS_SECURE_PAYMENT_CONFIRMATION_HELPER_H_
#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
namespace blink {
class ScriptValue;
class ExceptionState;
class SecurePaymentConfirmationHelper {
STATIC_ONLY(SecurePaymentConfirmationHelper);
public:
// Parse 'secure-payment-confirmation' data in |input| or throw an exception.
static void ParseSecurePaymentConfirmationData(const ScriptValue& input,
ExceptionState&);
};
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_MODULES_PAYMENTS_SECURE_PAYMENT_CONFIRMATION_HELPER_H_
// 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.
// https://github.com/rsolomakhin/secure-payment-confirmation#authenticating-a-payment
enum SecurePaymentConfirmationAction {
"authenticate",
};
dictionary SecurePaymentConfirmationRequest {
SecurePaymentConfirmationAction action;
required DOMString instrumentId;
// Opaque data about the current transaction provided by the issuer. As the
// issuer is the RP of the credential, |networkData| provides protection
// against replay attacks.
required BufferSource networkData;
unsigned long timeout;
required USVString fallbackUrl;
};
......@@ -1677,6 +1677,12 @@
{
name: "ScrollUnification",
},
{
name: "SecurePaymentConfirmation",
origin_trial_feature_name: "SecurePaymentConfirmation",
origin_trial_os: ["mac"],
status: "experimental",
},
{
name: "SendBeaconThrowForBlobWithNonSimpleType",
status: "stable",
......
<!DOCTYPE html>
<meta charset="utf-8">
<title>Test for 'secure-payment-confirmation' payment method</title>
<link rel="help" href="https://github.com/rsolomakhin/secure-payment-confirmation">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script>
'use strict';
const details = {total:
{label: 'Total', amount: {value: '0.01', currency: 'USD'}}};
test(() => {
new PaymentRequest([{
supportedMethods: 'secure-payment-confirmation',
data: {
// All valid parameters.
action: 'authenticate',
instrumentId: 'x',
networkData: Uint8Array.from('x', c => c.charCodeAt(0)),
timeout: 60000,
fallbackUrl: 'https://fallback.example/url'
},
}], details);
}, 'Valid payment method data does not throw exceptions.');
test(() => {
new PaymentRequest([{
supportedMethods: 'secure-payment-confirmation',
data: {
// Omitted action field.
instrumentId: 'x',
networkData: Uint8Array.from('x', c => c.charCodeAt(0)),
timeout: 60000,
fallbackUrl: 'https://fallback.example/url'
},
}], details);
}, 'The action field is optional.');
test(() => {
new PaymentRequest([{
supportedMethods: 'secure-payment-confirmation',
data: {
action: 'authenticate',
instrumentId: 'x',
networkData: Uint8Array.from('x', c => c.charCodeAt(0)),
// Omitted timeout field.
fallbackUrl: 'https://fallback.example/url'
},
}], details);
}, 'The timeout field is optional.');
test(() => {
assert_throws_js(TypeError, () => {
new PaymentRequest([{
supportedMethods: 'secure-payment-confirmation',
data: {
// Invalid action parameter.
action: 'authorize',
instrumentId: 'x',
networkData: Uint8Array.from('x', c => c.charCodeAt(0)),
timeout: 60000,
fallbackUrl: 'https://fallback.example/url'
},
}], details);
});
}, 'Invalid action parameter throws an exception.');
test(() => {
assert_throws_js(TypeError, () => {
new PaymentRequest([{
supportedMethods: 'secure-payment-confirmation',
data: {
action: 'authenticate',
// Omitted instrumentId field.
networkData: Uint8Array.from('x', c => c.charCodeAt(0)),
timeout: 60000,
fallbackUrl: 'https://fallback.example/url'
},
}], details);
});
}, 'The instrumentId field is required.');
test(() => {
assert_throws_js(TypeError, () => {
new PaymentRequest([{
supportedMethods: 'secure-payment-confirmation',
data: {
action: 'authenticate',
instrumentId: 'x',
// Omitted instrumentId field.
timeout: 60000,
fallbackUrl: 'https://fallback.example/url'
},
}], details);
});
}, 'The networkData field is required.');
test(() => {
assert_throws_js(TypeError, () => {
new PaymentRequest([{
supportedMethods: 'secure-payment-confirmation',
data: {
action: 'authenticate',
instrumentId: 'x',
networkData: Uint8Array.from('x', c => c.charCodeAt(0)),
timeout: 60000,
// Omitted fallbackUrl field.
},
}], details);
});
}, 'The fallbackUrl field is required.');
test(() => {
assert_throws_js(RangeError, () => {
new PaymentRequest([{
supportedMethods: 'secure-payment-confirmation',
data: {
action: 'authenticate',
// Empty instrumentId field.
instrumentId: '',
networkData: Uint8Array.from('x', c => c.charCodeAt(0)),
timeout: 60000,
fallbackUrl: 'https://fallback.example/url'
},
}], details);
});
}, 'Empty instrumentId field throws exception.');
test(() => {
assert_throws_js(TypeError, () => {
new PaymentRequest([{
supportedMethods: 'secure-payment-confirmation',
data: {
action: 'authenticate',
instrumentId: 'x',
// Null networkData field.
networkData: null,
timeout: 60000,
fallbackUrl: 'https://fallback.example/url'
},
}], details);
});
}, 'Null networkData field throws exception.');
test(() => {
assert_throws_js(TypeError, () => {
new PaymentRequest([{
supportedMethods: 'secure-payment-confirmation',
data: {
action: 'authenticate',
instrumentId: 'x',
// Empty networkData field.
networkData: [],
timeout: 60000,
fallbackUrl: 'https://fallback.example/url'
},
}], details);
});
}, 'Empty networkData field throws exception.');
test(() => {
assert_throws_js(RangeError, () => {
new PaymentRequest([{
supportedMethods: 'secure-payment-confirmation',
data: {
action: 'authenticate',
instrumentId: 'x',
networkData: Uint8Array.from('x', c => c.charCodeAt(0)),
// Timeout longer than 1 hour.
timeout: 1000 * 60 * 60 + 1,
fallbackUrl: 'https://fallback.example/url'
},
}], details);
});
}, 'Timeout longer than 1 hour throws exception.');
</script>
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