magento / magento/security-package

Duplicate reCAPTCHA validation breaks checkout success redirect after initial server error response

Open
#320 6 comments 1 reaction 1 assignee View on GitHub

@nathanjosiah is already working on this.

Since Nov 3, 2022.

Progress: dev in progress
Dominant language
PHP
Stars
75
Forks
73
PR merge metrics
No merged PRs in 30d

Description

### Preconditions (*)

1. Magento 2.4.3-p1
2. Enable for Checkout/Placing Order - reCAPTCHA v3 invisible

### Steps to reproduce (*)

1. Trigger a server side error on initial place order press
2. Fix server side error and place order again

### Expected result (*)

1. Order is placed
2. Redirect to success page

### Actual result (*)

1. Order is placed
2. No redirect to success page
3. Cart is cleared

### Diagnosis
This presented with a custom Payment Gateway module, it was triggered by bad AVS validation but I suspect that any server side error will cause this sequence of events.

Main problem is in Magento_ReCaptchaWebapiUi/js/webapiReCaptchaRegistry.js addListener method. If the token has already been gathered, it simply calls the callback function.

```
addListener: function (id, func) {
if (this.tokens.hasOwnProperty(id)) {
func(this.tokens[id]);
} else {
this._listeners[id] = func;
}
}
```

In Magento_ReCaptchaCheckout/js/model/place-order-mixin.js this means the payment-information URL gets called with the old token.

```
recaptchaRegistry.addListener('recaptcha-checkout-place-order', function (token) {
//Add reCaptcha value to place-order request and resolve deferred with the API call results
payload.xReCaptchaValue = token;
originalAction(serviceUrl, payload, messageContainer).done(function () {
recaptchaDeferred.resolve.apply(recaptchaDeferred, arguments);
}).fail(function () {
recaptchaDeferred.reject.apply(recaptchaDeferred, arguments);
});
});
```

As far as I am aware, a second validation request with the same token will always fail, so that request fails and rejects, completing the promise.

Meanwhile, place-order-mixin also triggers another validation which generates a new token.

```
recaptchaRegistry.triggers['recaptcha-checkout-place-order']();
```

When that returns it goes back into the callback, that request succeeds and the order is placed. However, the placeOrder deferred object has already been rejected with the original error.

It's not clear to me why you would ever want to reuse the old token, so I am able to solve this by simply changing the addListener method to this:

```
addListener: function (id, func) {
this._listeners[id] = func;
}
```

But I haven't studied the other reCAPTCHA modules enough to know if that will cause issues elsewhere. Perhaps v2 or checkbox should be handled differently.

Could potentially be solved with different logic in the place-order-mixin as well.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.