Commit 0d6f16784d1 for woocommerce
commit 0d6f16784d14010b7b5d3d17ec3002d100ef0567
Author: Michal Iwanow <4765119+mcliwanow@users.noreply.github.com>
Date: Mon Nov 2 08:58:15 2026 +0100
Ignore doing_wp_cron when checking WooCommerce.com request signatures (#70401)
* Ignore doing_wp_cron when checking WooCommerce.com request signatures
With ALTERNATE_WP_CRON on, WordPress answers a GET request with a redirect
to the same URL plus doing_wp_cron whenever cron is due. WooCommerce.com
follows it, and the extra arg no longer matches the signed URL, so the
request fails with request_verification_failed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Cover pretty-permalink requests in the doing_wp_cron signature test
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Herman <KokkieH@users.noreply.github.com>
diff --git a/plugins/woocommerce/changelog/wccom-3142-doing-wp-cron-signature b/plugins/woocommerce/changelog/wccom-3142-doing-wp-cron-signature
new file mode 100644
index 00000000000..a4340c34bea
--- /dev/null
+++ b/plugins/woocommerce/changelog/wccom-3142-doing-wp-cron-signature
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Fix WooCommerce.com requests such as connection status checks failing intermittently on stores that use ALTERNATE_WP_CRON.
diff --git a/plugins/woocommerce/includes/wccom-site/class-wc-wccom-site.php b/plugins/woocommerce/includes/wccom-site/class-wc-wccom-site.php
index 63b47249f88..076bf0604ae 100644
--- a/plugins/woocommerce/includes/wccom-site/class-wc-wccom-site.php
+++ b/plugins/woocommerce/includes/wccom-site/class-wc-wccom-site.php
@@ -229,7 +229,8 @@ class WC_WCCOM_Site {
// phpcs:disable WordPress.Security.ValidatedSanitizedInput.InputNotValidated, WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
$data = array(
'host' => $_SERVER['HTTP_HOST'],
- 'request_uri' => urldecode( remove_query_arg( array( 'token', 'signature' ), $_SERVER['REQUEST_URI'] ) ),
+ // With ALTERNATE_WP_CRON, WordPress redirects a GET to itself plus doing_wp_cron when cron is due, and WooCommerce.com follows it.
+ 'request_uri' => urldecode( remove_query_arg( array( 'token', 'signature', 'doing_wp_cron' ), $_SERVER['REQUEST_URI'] ) ),
'method' => strtoupper( $_SERVER['REQUEST_METHOD'] ),
);
// phpcs:enable
diff --git a/plugins/woocommerce/tests/php/includes/wccom-site/class-wc-wccom-site-test.php b/plugins/woocommerce/tests/php/includes/wccom-site/class-wc-wccom-site-test.php
index a54a2f4b542..bff37c773cd 100644
--- a/plugins/woocommerce/tests/php/includes/wccom-site/class-wc-wccom-site-test.php
+++ b/plugins/woocommerce/tests/php/includes/wccom-site/class-wc-wccom-site-test.php
@@ -234,4 +234,27 @@ class WC_WCCOM_Site_Test extends WC_Unit_Test_Case {
$this->assertNull( self::with_rest_route_context( '/wccom-site/v3/status', fn() => WC_WCCOM_Site::authentication_fallback( null ) ) );
$this->assertSame( $current_user_id, get_current_user_id() );
}
+
+ /**
+ * @testdox Should accept a signed request after the ALTERNATE_WP_CRON redirect adds doing_wp_cron, but no other change.
+ *
+ * @testWith ["/?rest_route=%2Fwccom-site%2Fv3%2Fstatus&doing_wp_cron=1761814536.1234567890123456789012", "/?rest_route=/wccom-site/v3/status", "/wccom-site/v3/status", true]
+ * ["/wp-json/wccom-site/v3/status?doing_wp_cron=1761814536.1234567890123456789012", "/wp-json/wccom-site/v3/status", "/wccom-site/v3/status", true]
+ * ["/?rest_route=/wccom-site/v3/ssr&doing_wp_cron=1761814536.1234567890123456789012", "/?rest_route=/wccom-site/v3/status", "/wccom-site/v3/ssr", false]
+ * ["/?rest_route=/wccom-site/v3/status&cron=1", "/?rest_route=/wccom-site/v3/status", "/wccom-site/v3/status", false]
+ *
+ * @param string $received_uri Request URI the site receives.
+ * @param string $signed_uri Request URI WooCommerce.com signed.
+ * @param string $route Route WordPress resolved.
+ * @param bool $expected Whether the connected user is authenticated.
+ */
+ public function test_authenticate_wccom_ignores_doing_wp_cron( string $received_uri, string $signed_uri, string $route, bool $expected ): void {
+ $user_id = self::factory()->user->create( array( 'role' => 'administrator' ) );
+ $this->simulate_request( $received_uri, true );
+ $this->sign_request_for_connected_site( $user_id, $signed_uri );
+
+ $user = self::with_rest_route_context( $route, fn() => WC_WCCOM_Site::authenticate_wccom( false ) );
+
+ $this->assertSame( $expected, $user instanceof WP_User && $user_id === $user->ID );
+ }
}