Recaptcha v2 throws an error when it is reset and its container element is removed from DOM
Asked Answered
U

2

21

When I explicitly render Recaptcha v2 and then reset it before removing it from the DOM, after ~40 seconds I get an error in the browser console.

I have a JSFiddle which can be used to reproduce the issue.

Here's the relevant code excerpt:

const recaptchaHolder = document.getElementById('...');
const recaptchaWidgetId = grecaptcha.render(recaptchaHolder, {/*...*/});
// then later
grecaptcha.reset(recaptchaWidgetId);
recaptchaHolder.parentElement.removeChild(recaptchaHolder);

The error that I get is:

Uncaught (in promise) Timeout

with the following stacktrace (the actual stacktrace might vary since it happens in heavily minified code):

setTimeout (async)      
J           @   recaptcha__en.js:100
(anonymous) @   recaptcha__en.js:285
tb          @   recaptcha__en.js:284
mj          @   recaptcha__en.js:456
(anonymous) @   recaptcha__en.js:458

I've tried to utilize the 'error-callback' parameter from the documentation to catch this error, but that did not help - the error is still uncaught, and this callback does not get invoked.

Another observation: if I omit calling grecaptcha.reset before removing the element from the DOM, then the error does not happen. However, it might result in inconsistent UI: if the user is challenged by recaptcha (for example, using images), and the element is simply removed from DOM without calling grecaptcha.reset, then the challenge HTML is not cleaned up from the DOM.

I'm looking for ways of either handling the above mentioned error, or handling recaptcha removal in a different way (if I am doing it wrong now)

UPDATE (June 24th 2022): This is still happening.

Unmentionable answered 3/10, 2018 at 0:2 Comment(0)
A
2

After many hours I have found only this solution, see https://jsfiddle.net/4mLhcksq/

There is setTimeout and 60 seconds (it could be shorter, I guess) pause before grecaptcha.reset() and then another pause before removing Recaptcha element. Actually, I had the same problem even if I do not remove Recaptcha element, only reset it.

const holder = getRecaptchaHolder();
holder.style.display = 'none'; //element disappears for users
setTimeout(function() {
  grecaptcha.reset(recaptchaWidgetId);
  setTimeout(function() { //we have to wait a while before removing element
    holder.parentElement.removeChild(holder);
  }, 1000);
}, 60000);

I am not especially proud of this solution. Let us hope somebody will provide better one.

Alejandrinaalejandro answered 9/11, 2018 at 17:30 Comment(1)
that actually works without the first setTiemout - the trick is to have the second setTimeout be executed after all the requests fired by grecaptcha.reset() have finished. Unfortunately, since the timing of those depends on user's network quality and/or processing power, that might be tricky to figure outUnmentionable
T
-4

The error is thrown because captcha has sent calls to google and it wants to receive response but as we remove the element so the response doesn't go according to plan and zone error is thrown, a better approach is to just hide the captcha once you have received the firebase.auth.signInWithPhoneNumber function's response. No need to set any timeouts and call captcha.clear() method in the destroy event of your page.

Trichroism answered 9/8, 2019 at 12:10 Comment(2)
What does firebase have to do with this issue? Could you also elaborate on why there is no need to clear the captcha? Because if it's not cleared, what can happen is that after recaptcha removal the "picture challenge" can stay on the pageUnmentionable
As i mentioned, firebase.auth.signInWithPhoneNumber which shows that i am talking about the captcha presented automatically by firebase for verification. Moreover, if we clear captcha right away after success it gives error, while if hide that captcha div, instead of removing it from the DOM then all the functionality works and nothing breaks.Trichroism

© 2022 - 2024 — McMap. All rights reserved.