diff --git a/projects/packages/cookie-consent/changelog/fix-consent-logger-nonce b/projects/packages/cookie-consent/changelog/fix-consent-logger-nonce new file mode 100644 index 000000000000..b6230661a8ac --- /dev/null +++ b/projects/packages/cookie-consent/changelog/fix-consent-logger-nonce @@ -0,0 +1,4 @@ +Significance: patch +Type: fixed + +Consent log: send a REST nonce for logged-in visitors so the consent row records the real user ID instead of 0. diff --git a/projects/packages/cookie-consent/src/class-cookie-consent.php b/projects/packages/cookie-consent/src/class-cookie-consent.php index dddd9653b7e1..6315a987bec6 100644 --- a/projects/packages/cookie-consent/src/class-cookie-consent.php +++ b/projects/packages/cookie-consent/src/class-cookie-consent.php @@ -1105,15 +1105,26 @@ public static function enqueue_assets() { // Resolve the configured Tracks event prefix once so it can be shared below. $config = self::get_config(); + $config_data = array( + 'apiUrl' => rest_url( 'jetpack/v4/cookie-consent/consent-log' ), + 'eventPrefix' => $config['event_prefix'], + ); + + // Only expose a REST nonce to logged-in visitors, so the consent logger can + // authenticate and record the real user_id. Anonymous visitors deliberately get + // none: their pages are full-page-cached, a cached nonce would go stale and make + // core reject the request (rest_cookie_invalid_nonce). Without a nonce core treats + // the request as anonymous and stores user_id = 0, which is correct for them. + if ( is_user_logged_in() ) { + $config_data['nonce'] = wp_create_nonce( 'wp_rest' ); + } + // Pass REST API URL and Tracks event prefix to the module via global config. wp_print_inline_script_tag( sprintf( 'window.jetpackCookieConsentConfig = %s;', wp_json_encode( - array( - 'apiUrl' => rest_url( 'jetpack/v4/cookie-consent/consent-log' ), - 'eventPrefix' => $config['event_prefix'], - ), + $config_data, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT ) ), diff --git a/projects/packages/cookie-consent/src/modules/cookie-consent/logger.ts b/projects/packages/cookie-consent/src/modules/cookie-consent/logger.ts index cc2bb44a796f..29165bcb87b6 100644 --- a/projects/packages/cookie-consent/src/modules/cookie-consent/logger.ts +++ b/projects/packages/cookie-consent/src/modules/cookie-consent/logger.ts @@ -24,12 +24,20 @@ async function logConsentEvent( return; } + const headers: Record< string, string > = { + 'Content-Type': 'application/json', + }; + // Send the REST nonce when present (logged-in visitors only) so the request + // authenticates and the consent row records the real user_id instead of 0. + const nonce = window.jetpackCookieConsentConfig?.nonce; + if ( nonce ) { + headers[ 'X-WP-Nonce' ] = nonce; + } + try { const response = await fetch( apiUrl, { method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, + headers, body: JSON.stringify( { event_type: eventType, url: window.location.href, diff --git a/projects/packages/cookie-consent/src/modules/cookie-consent/types.ts b/projects/packages/cookie-consent/src/modules/cookie-consent/types.ts index 6c0ae6c35898..e1f1198ac659 100644 --- a/projects/packages/cookie-consent/src/modules/cookie-consent/types.ts +++ b/projects/packages/cookie-consent/src/modules/cookie-consent/types.ts @@ -41,6 +41,7 @@ declare global { jetpackCookieConsentConfig?: { apiUrl: string; eventPrefix?: string; + nonce?: string; }; wp_set_consent?: ( category: string, value: 'allow' | 'deny' ) => void; wp_consent_type?: ConsentType;