Happy Tuesday!
all posts

Using reCAPTCHA v3 with Turbolinks

Published on Jan 22, 2019

B2a9eacd29aca2e5f9829fcbbd3fe552 Sven Pachnit GitHub Twitter StackOverflow

You might have noticed that calls to grecaptcha.execute will timeout after a turbolinks navigation.

The reason for this is simple, the badge is not in the DOM anymore. If you delete this element (.grecaptcha-badge) on the first load the same problem will occur.

The solution is rather simple but not really documented, it's hidden in the FAQ "The JavaScript API available for Invisible reCAPTCHA also works for v3."

So what do we need to do?

Unfortunately you will have to write some extra code in order to get this to work.

I assume your include looks something like this (but loading it via JS is the same deal)

<head>
  ...
  <script src='https://www.google.com/recaptcha/api.js?render=MY_PUBLIC_SITEKEY'></script>
</head>

Let's change it up a little:

  • replace your sitekey with "explicit"
  • define a onload parameter referencing a globally available callback function
<script src='https://www.google.com/recaptcha/api.js?render=explicit&onload=myCallback'></script>

In your layout add something like this (it must be on all sites you can "turbolinks" to, it must have an uniq ID in order for turbolinks-permanent to work)

<div id="my-grecaptcha-badge" data-turbolinks-permanent></div>

Then, in your callback function you will have to do two things:

  • Explicitely render the badge to your div created in the previous step
  • Save the result of the render in a variable that persists on turbolinks navigations (e.g. window, document)
function myCallback() {
  document.grecaptchaHandle = grecaptcha.render('my-grecaptcha-badge', {
    'sitekey': 'MY_PUBLIC_SITEKEY',
    'badge': 'inline', // must be inline
    'size': 'invisible' // must be invisible
  });
}

Then all you need to do is to make sure that your grecaptcha.execute calls don't use the sitekey as first parameter but the result of the render call.

  grecaptcha.execute(document.grecaptchaHandle).then(function(token) {
    console.log("executed", token)
  }

Voilà!

PS: If you want you can take a look at my AppOS module (internal framework but class should make sense nevertheless) for reCAPTCHA v3 (written in coffee) here: https://gist.github.com/2called-chaos/da02da3af297d4e39715ddfdd81ce7d4