Delete the captcha registration after retrieving data in Comment/Add and Message...
authorTim Düsterhus <duesterhus@woltlab.com>
Mon, 26 Jul 2021 12:42:44 +0000 (14:42 +0200)
committerTim Düsterhus <duesterhus@woltlab.com>
Mon, 26 Jul 2021 12:42:44 +0000 (14:42 +0200)
When a validation error is encountered, a new template with a new captcha will
be sent. However the logic within the captcha controller prevents a callback
from being added for a specific captcha ID if one is already registered. This
leads to the previous captcha callback being reused for another attempt.

This does not work, because a single instance of reCAPTCHA may only be used
once, thus erroring out if the callback is invoked a second time.

Fix this issue by deleting the captcha callback once we used it once. Upon
another failure another template will be sent, re-registering a new and valid
captcha.

It was also considered caching the return value, however this will cause issues
if the user mistypes a captcha as they will be unable to correct the error, due
to the same value being returned on the next attempt.

Ideally the `getData()` function would automatically delete the callback,
making it a single-use callback by design. This might break API users relying
on this current (broken) behavior, though.

The whole (AJAX) CAPTCHA API looks broken beyond repair. It also relies on the
jQuery parts being available. It should be cleanly refactored in a future
version.

ts/WoltLabSuite/Core/Ui/Comment/Add.ts
ts/WoltLabSuite/Core/Ui/Message/Reply.ts
wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Comment/Add.js
wcfsetup/install/files/js/WoltLabSuite/Core/Ui/Message/Reply.js

index 67c1049566a34bb39b11564f8dce9e6b8bf95b82..3018446c0f8bfe9fb52075c396e1fd86d4a0aa2b 100644 (file)
@@ -99,8 +99,10 @@ class UiCommentAdd {
       },
     };
 
-    if (ControllerCaptcha.has("commentAdd")) {
-      const data = ControllerCaptcha.getData("commentAdd");
+    const captchaId = "commentAdd";
+    if (ControllerCaptcha.has(captchaId)) {
+      const data = ControllerCaptcha.getData(captchaId);
+      ControllerCaptcha.delete(captchaId);
       if (data instanceof Promise) {
         void data.then((data) => {
           parameters = Core.extend(parameters, data) as ArbitraryObject;
index 4f8ed94003bce0c39eb766a3d7cfbef0ac66dcb2..9d10931f3663d1bb05c6a73f24b59bd5f9f6d61f 100644 (file)
@@ -119,6 +119,7 @@ class UiMessageReply {
     const captchaId = target.dataset.captchaId!;
     if (ControllerCaptcha.has(captchaId)) {
       const data = ControllerCaptcha.getData(captchaId);
+      ControllerCaptcha.delete(captchaId);
       if (data instanceof Promise) {
         void data.then((data) => {
           parameters = Core.extend(parameters, data) as ArbitraryObject;
index 31a67f4b059e2393d380b9cc186d0bb4e6c771bd..b7bfafc9d325a3ba1407a5476b71b4f8c60c1c93 100644 (file)
@@ -76,8 +76,10 @@ define(["require", "exports", "tslib", "../../Ajax", "../../Controller/Captcha",
                     },
                 },
             };
-            if (Captcha_1.default.has("commentAdd")) {
-                const data = Captcha_1.default.getData("commentAdd");
+            const captchaId = "commentAdd";
+            if (Captcha_1.default.has(captchaId)) {
+                const data = Captcha_1.default.getData(captchaId);
+                Captcha_1.default.delete(captchaId);
                 if (data instanceof Promise) {
                     void data.then((data) => {
                         parameters = Core.extend(parameters, data);
index ae6e5115ee9a05403662b09a94388aba166906bc..5f3fc97ab831154cfd274697460cee0777b17248 100644 (file)
@@ -80,6 +80,7 @@ define(["require", "exports", "tslib", "../../Ajax", "../../Core", "../../Event/
             const captchaId = target.dataset.captchaId;
             if (Captcha_1.default.has(captchaId)) {
                 const data = Captcha_1.default.getData(captchaId);
+                Captcha_1.default.delete(captchaId);
                 if (data instanceof Promise) {
                     void data.then((data) => {
                         parameters = Core.extend(parameters, data);