Skip to content
This repository was archived by the owner on Feb 23, 2024. It is now read-only.

Commit aafaf3e

Browse files
authored
Improve nonce handling by rejecting stale values (#3770)
* Improve nonce handling by rejecting previous nonces from cache * use timestamp instead of previous nonce * Switch back to time() * Seconds not ms * Add comment about the date code
1 parent a644268 commit aafaf3e

File tree

4 files changed

+58
-8
lines changed

4 files changed

+58
-8
lines changed

.eslintrc.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ module.exports = {
55
],
66
globals: {
77
wcStoreApiNonce: 'readonly',
8+
wcStoreApiNonceTimestamp: 'readonly',
89
fetchMock: true,
910
jQuery: 'readonly',
1011
IntersectionObserver: 'readonly',

assets/js/middleware/store-api-nonce.js

Lines changed: 52 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,18 @@
33
*/
44
import apiFetch from '@wordpress/api-fetch';
55

6-
// @ts-ignore wcStoreApiNonce is window global
7-
// Cache for the initial nonce initialized from hydration.
8-
let nonce = wcStoreApiNonce || '';
6+
// Stores the current nonce for the middleware.
7+
let currentNonce = '';
8+
let currentTimestamp = 0;
9+
10+
try {
11+
const storedNonceValue = window.localStorage.getItem( 'storeApiNonce' );
12+
const storedNonce = storedNonceValue ? JSON.parse( storedNonceValue ) : {};
13+
currentNonce = storedNonce?.nonce || '';
14+
currentTimestamp = storedNonce?.timestamp || 0;
15+
} catch {
16+
// We can ignore an error from JSON parse.
17+
}
918

1019
/**
1120
* Returns whether or not this is a non GET wc/store API request.
@@ -28,12 +37,44 @@ const isStoreApiGetRequest = ( options ) => {
2837
* @param {Object} headers Headers object.
2938
*/
3039
const setNonce = ( headers ) => {
31-
const newNonce = headers?.get( 'X-WC-Store-API-Nonce' );
32-
if ( newNonce ) {
33-
nonce = newNonce;
40+
const nonce = headers?.get( 'X-WC-Store-API-Nonce' ) || '';
41+
const timestamp = headers?.get( 'X-WC-Store-API-Nonce-Timestamp' ) || 0;
42+
43+
if ( nonce ) {
44+
updateNonce( nonce, timestamp );
3445
}
3546
};
3647

48+
/**
49+
* Updates the stored nonce within localStorage so it is persisted between page loads.
50+
*
51+
* @param {string} nonce Incoming nonce string.
52+
* @param {number} timestamp Timestamp from server of nonce.
53+
*/
54+
const updateNonce = ( nonce, timestamp ) => {
55+
// If the "new" nonce matches the current nonce, we don't need to update.
56+
if ( nonce === currentNonce ) {
57+
return;
58+
}
59+
60+
// Only update the nonce if newer. It might be coming from cache.
61+
if ( currentTimestamp && timestamp < currentTimestamp ) {
62+
return;
63+
}
64+
65+
currentNonce = nonce;
66+
currentTimestamp = timestamp || Date.now() / 1000; // Convert ms to seconds to match php time()
67+
68+
// Update the persisted values.
69+
window.localStorage.setItem(
70+
'storeApiNonce',
71+
JSON.stringify( {
72+
nonce: currentNonce,
73+
timestamp: currentTimestamp,
74+
} )
75+
);
76+
};
77+
3778
/**
3879
* Nonce middleware which updates the nonce after a request, if given.
3980
*
@@ -47,11 +88,15 @@ const storeNonceMiddleware = ( options, next ) => {
4788
const existingHeaders = options.headers || {};
4889
options.headers = {
4990
...existingHeaders,
50-
'X-WC-Store-API-Nonce': nonce,
91+
'X-WC-Store-API-Nonce': currentNonce,
5192
};
5293
}
5394
return next( options, next );
5495
};
5596

5697
apiFetch.use( storeNonceMiddleware );
5798
apiFetch.setNonce = setNonce;
99+
100+
// @ts-ignore wcStoreApiNonce is window global cache for the initial nonce initialized from hydration.
101+
// @ts-ignore wcStoreApiNonceTimestamp is window global cache for the initial nonce initialized from hydration.
102+
updateNonce( wcStoreApiNonce, wcStoreApiNonceTimestamp );

src/Assets.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,10 @@ public static function register_assets() {
6363
// Inline data.
6464
wp_add_inline_script(
6565
'wc-blocks-middleware',
66-
"var wcStoreApiNonce = '" . esc_js( wp_create_nonce( 'wc_store_api' ) ) . "';",
66+
"
67+
var wcStoreApiNonce = '" . esc_js( wp_create_nonce( 'wc_store_api' ) ) . "';
68+
var wcStoreApiNonceTimestamp = '" . esc_js( time() ) . "';
69+
",
6770
'before'
6871
);
6972

src/StoreApi/Routes/AbstractRoute.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ public function get_response( \WP_REST_Request $request ) {
8181
}
8282

8383
$response->header( 'X-WC-Store-API-Nonce', wp_create_nonce( 'wc_store_api' ) );
84+
$response->header( 'X-WC-Store-API-Nonce-Timestamp', time() );
8485
$response->header( 'X-WC-Store-API-User', get_current_user_id() );
8586
return $response;
8687
}

0 commit comments

Comments
 (0)