MAC
MAC

Reputation: 71

Both reCaptcha v2 and v3 on same page

as I understand, it seems to be possible to use both v2 and v3 on the same page (see https://github.com/google/recaptcha/issues/279), but can't find any example of it :/

I already have HTML pages with invisible v2, and first, I want to evaluate the accuracy of the v3 score without breaking my current v2 (v3 will be only for test purposes). I was also wondering if it could be possible to use v3 and render v2 challenges for low scores.

Any idea ?

(sorry for my english, it's not my mother tongue ;)

Upvotes: 7

Views: 9231

Answers (3)

MarkWPiper
MarkWPiper

Reputation: 923

V2 and V3 can be used on the same page. This can be done while loading recaptcha/api.js only once (instead of once per version) — but it requires a particular loading sequence to avoid race conditions.

  • The race condition may show up as the error Invalid site key or not loaded in api.js or as the wrong sitekey version being passed in verify API calls.

Unfortunately, Google's documentation doesn't address this fully: https://developers.google.com/recaptcha/docs/loading#loading_recaptcha_asynchronously

Here's what to do instead:

  1. Load a script in your head to define an onload callback and grecaptcha.ready. Instead of Google's approach of checking grecaptcha === 'undefined' within ready, you should check !window.recaptchaLoaded to avoid race conditions.
window.recaptchaLoaded = false;

window.onRecaptchaLoaded = function() {
  window.recaptchaLoaded = true;
}

if (typeof grecaptcha === 'undefined') {
  grecaptcha = {
    ready: function(cb) {
      if (!window.recaptchaLoaded) {
        const c = '___grecaptcha_cfg';
        window[c] = window[c] || {};
        (window[c]['fns'] = window[c]['fns']||[]).push(cb);
      } else {
        cb();
      }
    }
  };
}
  1. Use the onload option and load the recaptcha script with your V3 sitekey, async or deferred:
<script defer src="https://www.google.com/recaptcha/api.js?onload=onRecaptchaLoaded&render=V3_SITE_KEY"></script>
  1. To use V2, call grecaptcha.render with your V2 sitekey, wrapped in grecaptcha.ready
grecaptcha.ready(function() {
  grecaptcha.render("g-recaptcha", {
      'sitekey': 'V2_SITE_KEY'
  });
});

To use V3, call grecaptcha.execute with your V3 sitekey, wrapped in grecaptcha.ready

grecaptcha.ready(() => {
  grecaptcha.execute('V3_SITE_KEY', {action: 'your_action'}).then((token) => {
    ...
  });
});

This solution works by loading reCAPTCHA asynchronously, using explicit rendering of the V2 reCAPTCHA widget, and programatically invoking the V3 challenge.

Upvotes: 3

Marc Annous
Marc Annous

Reputation: 527

Yes, this explains how : Can I run reCAPTCHA v2 and v3 on the same page?

Worked well with two v3 and one v2 checkbox on the same page.

Upvotes: 4

Lucas Caton
Lucas Caton

Reputation: 3255

The following example (written by Anton Cherniavskyi) works.

However, I think it only works with v2 invisible + v3, ie.: You can't use v2 checkbox + v3.

<script src="https://www.google.com/recaptcha/api.js?onload=v2_onload"></script>
<script src="https://www.google.com/recaptcha/api.js?onload=v3_onload&render=V3_SITE_KEY"></script>
<div class="g-recaptcha" data-size="invisible" data-sitekey="V2_INVISIBLE_SITE_KEY" data-callback="v2_callback"></div>
<script type="text/javascript">
    function v2_onload() { console.log('v2 loaded'); }
    function v3_onload() { console.log('v3 loaded'); }
    function v2_callback(token) { console.log('v2 token: ' + token); }
    function v3_callback(token) { console.log('v3 token: ' + token); }

    // call these manually
    function test_v2() { grecaptcha.execute(); }
    function test_v3() { grecaptcha.execute(V3_SITE_KEY/*, {action: '...'}*/).then(v3_callback); }
</script>

Upvotes: 2

Related Questions