Improve nonce handling by rejecting stale values (https://github.com/woocommerce/woocommerce-blocks/pull/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
This commit is contained in:
parent
799e6f26fe
commit
d9e2f62540
|
@ -5,6 +5,7 @@ module.exports = {
|
|||
],
|
||||
globals: {
|
||||
wcStoreApiNonce: 'readonly',
|
||||
wcStoreApiNonceTimestamp: 'readonly',
|
||||
fetchMock: true,
|
||||
jQuery: 'readonly',
|
||||
IntersectionObserver: 'readonly',
|
||||
|
|
|
@ -3,9 +3,18 @@
|
|||
*/
|
||||
import apiFetch from '@wordpress/api-fetch';
|
||||
|
||||
// @ts-ignore wcStoreApiNonce is window global
|
||||
// Cache for the initial nonce initialized from hydration.
|
||||
let nonce = wcStoreApiNonce || '';
|
||||
// Stores the current nonce for the middleware.
|
||||
let currentNonce = '';
|
||||
let currentTimestamp = 0;
|
||||
|
||||
try {
|
||||
const storedNonceValue = window.localStorage.getItem( 'storeApiNonce' );
|
||||
const storedNonce = storedNonceValue ? JSON.parse( storedNonceValue ) : {};
|
||||
currentNonce = storedNonce?.nonce || '';
|
||||
currentTimestamp = storedNonce?.timestamp || 0;
|
||||
} catch {
|
||||
// We can ignore an error from JSON parse.
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not this is a non GET wc/store API request.
|
||||
|
@ -28,12 +37,44 @@ const isStoreApiGetRequest = ( options ) => {
|
|||
* @param {Object} headers Headers object.
|
||||
*/
|
||||
const setNonce = ( headers ) => {
|
||||
const newNonce = headers?.get( 'X-WC-Store-API-Nonce' );
|
||||
if ( newNonce ) {
|
||||
nonce = newNonce;
|
||||
const nonce = headers?.get( 'X-WC-Store-API-Nonce' ) || '';
|
||||
const timestamp = headers?.get( 'X-WC-Store-API-Nonce-Timestamp' ) || 0;
|
||||
|
||||
if ( nonce ) {
|
||||
updateNonce( nonce, timestamp );
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Updates the stored nonce within localStorage so it is persisted between page loads.
|
||||
*
|
||||
* @param {string} nonce Incoming nonce string.
|
||||
* @param {number} timestamp Timestamp from server of nonce.
|
||||
*/
|
||||
const updateNonce = ( nonce, timestamp ) => {
|
||||
// If the "new" nonce matches the current nonce, we don't need to update.
|
||||
if ( nonce === currentNonce ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Only update the nonce if newer. It might be coming from cache.
|
||||
if ( currentTimestamp && timestamp < currentTimestamp ) {
|
||||
return;
|
||||
}
|
||||
|
||||
currentNonce = nonce;
|
||||
currentTimestamp = timestamp || Date.now() / 1000; // Convert ms to seconds to match php time()
|
||||
|
||||
// Update the persisted values.
|
||||
window.localStorage.setItem(
|
||||
'storeApiNonce',
|
||||
JSON.stringify( {
|
||||
nonce: currentNonce,
|
||||
timestamp: currentTimestamp,
|
||||
} )
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Nonce middleware which updates the nonce after a request, if given.
|
||||
*
|
||||
|
@ -47,7 +88,7 @@ const storeNonceMiddleware = ( options, next ) => {
|
|||
const existingHeaders = options.headers || {};
|
||||
options.headers = {
|
||||
...existingHeaders,
|
||||
'X-WC-Store-API-Nonce': nonce,
|
||||
'X-WC-Store-API-Nonce': currentNonce,
|
||||
};
|
||||
}
|
||||
return next( options, next );
|
||||
|
@ -55,3 +96,7 @@ const storeNonceMiddleware = ( options, next ) => {
|
|||
|
||||
apiFetch.use( storeNonceMiddleware );
|
||||
apiFetch.setNonce = setNonce;
|
||||
|
||||
// @ts-ignore wcStoreApiNonce is window global cache for the initial nonce initialized from hydration.
|
||||
// @ts-ignore wcStoreApiNonceTimestamp is window global cache for the initial nonce initialized from hydration.
|
||||
updateNonce( wcStoreApiNonce, wcStoreApiNonceTimestamp );
|
||||
|
|
|
@ -63,7 +63,10 @@ class Assets {
|
|||
// Inline data.
|
||||
wp_add_inline_script(
|
||||
'wc-blocks-middleware',
|
||||
"var wcStoreApiNonce = '" . esc_js( wp_create_nonce( 'wc_store_api' ) ) . "';",
|
||||
"
|
||||
var wcStoreApiNonce = '" . esc_js( wp_create_nonce( 'wc_store_api' ) ) . "';
|
||||
var wcStoreApiNonceTimestamp = '" . esc_js( time() ) . "';
|
||||
",
|
||||
'before'
|
||||
);
|
||||
|
||||
|
|
|
@ -81,6 +81,7 @@ abstract class AbstractRoute implements RouteInterface {
|
|||
}
|
||||
|
||||
$response->header( 'X-WC-Store-API-Nonce', wp_create_nonce( 'wc_store_api' ) );
|
||||
$response->header( 'X-WC-Store-API-Nonce-Timestamp', time() );
|
||||
$response->header( 'X-WC-Store-API-User', get_current_user_id() );
|
||||
return $response;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue