- Issue created by @glynster
- πΊπΈUnited States glynster
I am debugging at the moment to try and be useful. There are no console errors. However here are some observations:
Locally it does load fine.
On production it does not.
Markup wise I can see<div class="cf-turnstile" data-sitekey="################" data-theme="light" data-size="normal" data-language="auto" data-retry="auto" interval="8000" data-appearance="always"></div>
but on version 1.1.13 the markup is:
<div class="cf-turnstile" data-sitekey="################" data-theme="light" data-size="normal" data-language="auto" data-retry="auto" interval="8000" data-appearance="always"><div><input type="hidden" name="cf-turnstile-response" id="cf-chl-widget-b5dkd_response" value="TOKEN"></div></div>
- πΊπΈUnited States greatmatter
I think I got it--I've just pushed a hotfix to 1.1.16
- πΊπΈUnited States glynster
So it seems this actually fixes the issue:
1οΈβ£ Run This in Browser Console to Force a Reset
document.querySelectorAll('.cf-turnstile').forEach(el => {
turnstile.remove(el);
});
turnstile.render('.cf-turnstile', { sitekey: "######" });2. Modify the Form Rendering Code to Prevent Duplicate Initialization
To avoid this problem, modify your Drupal form rendering:
$form['turnstile_widget'] = [ '#markup' => '<div id="turnstile-container" class="cf-turnstile" data-sitekey="' . turnstile_site_key() . '"></div>', ]; $form['#attached']['drupalSettings']['turnstile'] = [ 'callback' => 'turnstileSuccess', 'errorCallback' => 'turnstileError', ]; $form['#attached']['library'][] = 'turnstile/turnstile';
Then add this script in turnstile.ajax.js to remove any existing Turnstile instances before rendering a new one:
document.addEventListener("DOMContentLoaded", function () { const turnstileContainer = document.querySelector('.cf-turnstile'); if (turnstileContainer) { turnstile.remove(turnstileContainer); turnstile.render(turnstileContainer, { sitekey: drupalSettings.turnstile.sitekey }); } });
- πΊπΈUnited States greatmatter
Would you mind letting me know if 1.1.16 fixes it for you?
- πΊπΈUnited States glynster
@greatmatter thanks so much for being so responsive. I just deployed with your new hotfix and sadly I get the same results, fails to load/initialize.
- πΊπΈUnited States greatmatter
How odd--I've tested it across our sites--would you mind clearing your caches (including edge caches, if any)?
- πΊπΈUnited States glynster
No luck on my end We have cleared caches Drupal, Varnish, Cloudflare, etc. The only fix is this:
turnstile.render('.cf-turnstile', { sitekey: "#################" });
With the current version or revert to 1.1.13.
- πΊπΈUnited States greatmatter
Please try resaving your keys--I'm wondering if something weird happened with the upgrading/downgrading.
- πΊπΈUnited States glynster
I wish I could have good news and it be that simple but it is not.
This seems to work:
/** * @file * JavaScript behaviors for Turnstile. */ (function ($, Drupal, once) { 'use strict'; /** * Ensures Turnstile renders correctly on page load & AJAX events. */ Drupal.behaviors.turnstileAutoRender = { attach: function (context) { once('turnstile-init', '.cf-turnstile', context).forEach((el) => { turnstile.render(el); }); } }; /** * Re-renders Turnstile when an AJAX request updates the form. */ Drupal.AjaxCommands.prototype.turnstileRender = function () { $('.cf-turnstile').each(function(){ turnstile.render(this); }); }; })(jQuery, Drupal, once);
- πΊπΈUnited States greatmatter
I cannot seem to replicate this issue. The code you sent will likely break the Form/AJAX functionality (using EventSubscriber) introduced in 1.1.10.
I'm wondering if, for some reason, the
isn't getting automatically called by the script. Do you have multiple CAPTCHAs on the same page? - πΊπΈUnited States glynster
Only 1, nothing fancy going on here. These are just basic webforms and GIn Login pages.
- πΊπΈUnited States glynster
Okay well all our sites are behind Cloudflare and as soon as we disable RocketLoader it works. But we have always used this service. Since the change this is now conflicting it seems.
- πΊπΈUnited States keiserjb
I was getting cookie errors in the Chrome console.
Reading cookie in cross-site context may be impacted on Chrome
Cookies with the SameSite=None; Secure and not Partitioned attributes that operate in cross-site contexts are third-party cookies. Chrome is moving towards a new experience that allows users to choose to browse without third-party cookies.Learn more from the linked article about preparing your site to avoid potential breakage.
It listed multiple cookies from the the cloudflare domain.
Thanks for the tip about Rocket Loader in Cloudflare. I turned that off and it started working.
- πΊπΈUnited States greatmatter
Hm. I wonder why it's working in our sites. We're behind Cloudflare, but don't use RocketLoader. We also have SameSite=Lax - so I'm wondering if that plays a role. I'm open to ideas of how to make this work.
@keiserjb - what errors are you getting in the console?
The big change between versions was how the main Cloudflare Turnstile script was being included. We could add to the turnstile_library_info_build() the defer and async attributes--would you mind testing that to let me know if that changes anything? I'm not going to make a patch before knowing if it works for you.
- πΊπΈUnited States glynster
@greatmatter you are referring to the services file right?
parameters: session.storage.options: cookie_samesite: Lax
We already have this set on our sites, so we can rule that one out.
I can confirm that updating the widget does resolve the issue
/** * Build the Turnstile captcha form. * * @return mixed * The return value. */ public function getWidget($validation_function) { // Add Turnstile script with async and defer attributes. $widget['form']['#attached']['html_head'][] = [ [ '#tag' => 'script', '#attributes' => [ 'src' => 'https://challenges.cloudflare.com/turnstile/v0/api.js', 'async' => 'async', 'defer' => 'defer', ], ], 'turnstile_api', ]; // Captcha requires TRUE to be returned in solution. $widget['solution'] = TRUE; $widget['captcha_validate'] = $validation_function; $widget['form']['captcha_response'] = [ '#type' => 'hidden', '#value' => self::EMPTY_CAPTCHA_RESPONSE, ]; // Allows the CAPTCHA to be displayed on cached pages. $widget['cacheable'] = TRUE; // Ensure the widget is properly rendered. $widget['form']['turnstile_widget'] = [ '#markup' => '<div' . $this->getAttributesString() . '></div>', ]; return $widget; }
- πΊπΈUnited States glynster
Yes this suggestions resolves the issue as well
/** * Implements hook_library_info_build(). */ function turnstile_library_info_build() { $config = \Drupal::config('turnstile.settings'); $turnstile_src = $config->get('turnstile_src'); $libraries = []; $libraries['turnstile.remote'] = [ 'js' => [ $turnstile_src => [ 'type' => 'external', 'attributes' => [ 'defer' => TRUE, 'async' => TRUE, ], ], ], ]; return $libraries; }
- πΊπΈUnited States keiserjb
Changing hook_library_info_build works for me as well with 1.1.16.
I had trouble replicating this problem after discovery last week as it still worked locally and on my dev site. Today, I was able to find out how to break Turnstile on my dev site by adding a domain. Then I got the cross-site cookie errors just like production. The js alterations fix it though.
- πΊπΈUnited States greatmatter
Ok, it's all fixed in 1.1.17. Thanks, everyone!
- πΊπΈUnited States glynster
Rockstar thank you so much for pushing this fix.