From dcc725868ae2a79ec9275f98f8937429a29b00c0 Mon Sep 17 00:00:00 2001 From: goldenapples Date: Tue, 10 Mar 2026 09:43:55 -0400 Subject: [PATCH 1/2] Reuse existing session token when refreshing sessions Fixes a bug when the rolling sessions functionality would update a user's session token on every pageview, invalidating any nonce checks before they're run. This change preserves the existing session token by passing it in to wp_set_auth_cookie(). In addition, the lifetime of the token itself needs to be extended so that it doesn't expire before the logged-in cookies. --- src/Actions/Authentication.php | 35 +++++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/src/Actions/Authentication.php b/src/Actions/Authentication.php index f6fb41c94..6ff07ac82 100644 --- a/src/Actions/Authentication.php +++ b/src/Actions/Authentication.php @@ -579,12 +579,37 @@ public function onShutdown(): void if ('false' !== $this->getPlugin()->getOption('sessions', 'rolling_sessions')) { $store = $this->getSdk()->configuration()->getSessionStorage(); - /** - * @var CookieStore $store - */ - $store->setState(true); + if ($store instanceof CookieStore) { + $store->setState(true); + } + + // Extend the existing session token's expiry and reissue the browser cookies. + // + // The naive approach — calling wp_set_auth_cookie() with no token — rotates the + // session token. Because nonces are cryptographically bound to the session token, + // any nonce generated during the current request (with the old token) becomes + // invalid the moment the new cookie is sent, causing REST requests to fail with + // rest_cookie_invalid_nonce. + // + // wp_set_auth_cookie() accepts a $token argument. Passing the existing token + // reissues the browser cookies with a fresh expiration without rotating the token, + // so nonces remain valid and the full rolling-session behaviour is preserved. + $userId = get_current_user_id(); + $token = wp_get_session_token(); + + if ('' !== $token) { + /** This filter is documented in wp-includes/pluggable.php */ + $expiration = apply_filters('auth_cookie_expiration', 14 * DAY_IN_SECONDS, $userId, true); + + // Update the server-side session record (wp_set_auth_cookie does not do this + // when a token is supplied). + \WP_Session_Tokens::get_instance($userId)->update($token, [ + 'expiration' => time() + $expiration, + ]); - wp_set_auth_cookie(get_current_user_id(), true); + // Reissue the browser cookies with the same token and new expiration. + wp_set_auth_cookie($userId, true, '', $token); + } } } From dfbde56277ce99b95a345ca8cb6981e6558904df Mon Sep 17 00:00:00 2001 From: goldenapples Date: Thu, 12 Mar 2026 09:30:26 -0400 Subject: [PATCH 2/2] Update only the expiration field of the token Because WP_Session_Tokens::update() overwrites the entire session object in its datastore, the previous code was wiping out fields like IP address and original login date as well as any custom data attached to the token. This reads the original token data array so that only the exp field is updated. --- src/Actions/Authentication.php | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/Actions/Authentication.php b/src/Actions/Authentication.php index 6ff07ac82..c008c64d3 100644 --- a/src/Actions/Authentication.php +++ b/src/Actions/Authentication.php @@ -601,11 +601,17 @@ public function onShutdown(): void /** This filter is documented in wp-includes/pluggable.php */ $expiration = apply_filters('auth_cookie_expiration', 14 * DAY_IN_SECONDS, $userId, true); - // Update the server-side session record (wp_set_auth_cookie does not do this - // when a token is supplied). - \WP_Session_Tokens::get_instance($userId)->update($token, [ - 'expiration' => time() + $expiration, - ]); + // Update the expiration date in the server-side session record + // (wp_set_auth_cookie does not do this when a token is supplied). + $manager = \WP_Session_Tokens::get_instance( $userId ); + $session = $manager->get( $token ); + + if ( ! is_array( $session ) ) { + return; + } + + $session['expiration'] = time() + $expiration; + $manager->update( $token, $session ); // Reissue the browser cookies with the same token and new expiration. wp_set_auth_cookie($userId, true, '', $token);