Commit 26f01dec553 for woocommerce
commit 26f01dec553f3868c674f4ef605c63606975c5ea
Author: James Kemp <me@jckemp.com>
Date: Tue Nov 10 09:00:48 2026 +0000
Fix My Account login silently failing with a stale session cookie (#70785)
* Fix My Account login silently failing with a stale session cookie
* Add changefile(s) from automation for the following project(s): woocommerce
* Address review feedback
---------
Co-authored-by: woocommercebot <woocommercebot@users.noreply.github.com>
diff --git a/plugins/woocommerce/changelog/70785-fix-70762-my-account-login-silently-fails-page b/plugins/woocommerce/changelog/70785-fix-70762-my-account-login-silently-fails-page
new file mode 100644
index 00000000000..093cd518573
--- /dev/null
+++ b/plugins/woocommerce/changelog/70785-fix-70762-my-account-login-silently-fails-page
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Fix My Account login and registration silently failing when a shopper's session cookie can no longer be verified after the site's security keys change.
diff --git a/plugins/woocommerce/includes/class-wc-session-handler.php b/plugins/woocommerce/includes/class-wc-session-handler.php
index 437e7c16815..60a22e51e5a 100644
--- a/plugins/woocommerce/includes/class-wc-session-handler.php
+++ b/plugins/woocommerce/includes/class-wc-session-handler.php
@@ -166,6 +166,13 @@ class WC_Session_Handler extends WC_Session {
$cookie = $this->get_session_cookie();
if ( ! $cookie ) {
+ // A cookie that is present but cannot be verified (e.g. the site's salts changed) is not a session.
+ // Expire it and drop it from this request so has_session() doesn't treat it as one.
+ if ( isset( $_COOKIE[ $this->_cookie ] ) ) {
+ wc_setcookie( $this->_cookie, '', time() - YEAR_IN_SECONDS, $this->use_secure_cookie(), true );
+ unset( $_COOKIE[ $this->_cookie ] );
+ }
+
// If there is no cookie, generate a new session/customer ID.
$this->_customer_id = $this->generate_customer_id();
$this->_data = $this->get_session_data();
diff --git a/plugins/woocommerce/tests/php/includes/class-wc-session-concurrency-test.php b/plugins/woocommerce/tests/php/includes/class-wc-session-concurrency-test.php
index 999942388a0..f14e2db00cf 100644
--- a/plugins/woocommerce/tests/php/includes/class-wc-session-concurrency-test.php
+++ b/plugins/woocommerce/tests/php/includes/class-wc-session-concurrency-test.php
@@ -146,6 +146,78 @@ class WC_Session_Concurrency_Test extends WC_Unit_Test_Case {
);
}
+ /**
+ * @testdox A session cookie that fails verification is dropped from the request, so the visitor has no session.
+ */
+ public function test_unverifiable_session_cookie_is_not_treated_as_a_session(): void {
+ $cookie_name = 'wp_woocommerce_session_' . COOKIEHASH;
+ $_COOKIE[ $cookie_name ] = 't_0123456789abcdef0123456789abcd|' . ( time() + DAY_IN_SECONDS ) . '|' . ( time() + HOUR_IN_SECONDS ) . '|invalidhash';
+
+ // Avoid sending headers from the test process.
+ add_filter( 'woocommerce_set_cookie_enabled', '__return_false' );
+
+ try {
+ $handler = new WC_Session_Handler();
+ $handler->init_session_cookie();
+
+ $this->assertArrayNotHasKey( $cookie_name, $_COOKIE, 'The unverifiable cookie should be removed from the request' );
+ $this->assertFalse( $handler->has_session(), 'An unverifiable cookie should not count as a session' );
+ } finally {
+ remove_filter( 'woocommerce_set_cookie_enabled', '__return_false' );
+ unset( $_COOKIE[ $cookie_name ] );
+ }
+ }
+
+ /**
+ * @testdox A validly signed session cookie is kept in the request and counts as a session.
+ */
+ public function test_verifiable_session_cookie_is_kept_as_a_session(): void {
+ $cookie_name = 'wp_woocommerce_session_' . COOKIEHASH;
+ $customer_id = 't_0123456789abcdef0123456789abcd';
+ $expiration = time() + DAY_IN_SECONDS;
+ $expiring = time() + HOUR_IN_SECONDS;
+ $message = $customer_id . '|' . $expiration;
+ $hash = hash_hmac( 'md5', $message, wp_hash( $message ) );
+
+ $_COOKIE[ $cookie_name ] = $message . '|' . $expiring . '|' . $hash;
+
+ // Avoid sending headers from the test process.
+ add_filter( 'woocommerce_set_cookie_enabled', '__return_false' );
+
+ try {
+ $handler = new WC_Session_Handler();
+ $handler->init_session_cookie();
+
+ $this->assertArrayHasKey( $cookie_name, $_COOKIE, 'A verifiable cookie should stay in the request' );
+ $this->assertTrue( $handler->has_session(), 'A verifiable cookie should count as a session' );
+ } finally {
+ remove_filter( 'woocommerce_set_cookie_enabled', '__return_false' );
+ unset( $_COOKIE[ $cookie_name ] );
+ }
+ }
+
+ /**
+ * @testdox A session cookie with the wrong number of parts is dropped from the request, so the visitor has no session.
+ */
+ public function test_malformed_session_cookie_is_not_treated_as_a_session(): void {
+ $cookie_name = 'wp_woocommerce_session_' . COOKIEHASH;
+ $_COOKIE[ $cookie_name ] = 't_0123456789abcdef0123456789abcd|' . ( time() + DAY_IN_SECONDS );
+
+ // Avoid sending headers from the test process.
+ add_filter( 'woocommerce_set_cookie_enabled', '__return_false' );
+
+ try {
+ $handler = new WC_Session_Handler();
+ $handler->init_session_cookie();
+
+ $this->assertArrayNotHasKey( $cookie_name, $_COOKIE, 'The malformed cookie should be removed from the request' );
+ $this->assertFalse( $handler->has_session(), 'A malformed cookie should not count as a session' );
+ } finally {
+ remove_filter( 'woocommerce_set_cookie_enabled', '__return_false' );
+ unset( $_COOKIE[ $cookie_name ] );
+ }
+ }
+
/**
* Seeds the session, then interleaves two requests: load A, load B, save A, save B.
*