Skip to content

Commit

Permalink
API Fetch: refresh nonces as soon as they expired, then fetch again (#…
Browse files Browse the repository at this point in the history
…16683)

* API Fetch: refresh nonces as soon as they expired, then fetch again

* Use exit()

* Fix PHP linting errors
  • Loading branch information
ellatrix committed Aug 9, 2019
1 parent 60ec8fe commit be95231
Show file tree
Hide file tree
Showing 5 changed files with 93 additions and 27 deletions.
8 changes: 8 additions & 0 deletions gutenberg.php
Original file line number Diff line number Diff line change
Expand Up @@ -128,3 +128,11 @@ function gutenberg_pre_init() {

require_once dirname( __FILE__ ) . '/lib/load.php';
}

/**
* Outputs a WP REST API nonce.
*/
function gutenberg_rest_nonce() {
exit( wp_create_nonce( 'wp_rest' ) );
}
add_action( 'wp_ajax_gutenberg_rest_nonce', 'gutenberg_rest_nonce' );
24 changes: 5 additions & 19 deletions lib/client-assets.php
Original file line number Diff line number Diff line change
Expand Up @@ -246,25 +246,11 @@ function gutenberg_register_scripts_and_styles() {
wp_add_inline_script(
'wp-api-fetch',
sprintf(
implode(
"\n",
array(
'( function() {',
' var nonceMiddleware = wp.apiFetch.createNonceMiddleware( "%s" );',
' wp.apiFetch.use( nonceMiddleware );',
' wp.hooks.addAction(',
' "heartbeat.tick",',
' "core/api-fetch/create-nonce-middleware",',
' function( response ) {',
' if ( response[ "rest_nonce" ] ) {',
' nonceMiddleware.nonce = response[ "rest_nonce" ];',
' }',
' }',
' )',
'} )();',
)
),
( wp_installing() && ! is_multisite() ) ? '' : wp_create_nonce( 'wp_rest' )
'wp.apiFetch.nonceMiddleware = wp.apiFetch.createNonceMiddleware( "%s" );' .
'wp.apiFetch.use( wp.apiFetch.nonceMiddleware );' .
'wp.apiFetch.nonceEndpoint = "%s";',
( wp_installing() && ! is_multisite() ) ? '' : wp_create_nonce( 'wp_rest' ),
admin_url( 'admin-ajax.php?action=gutenberg_rest_nonce' )
),
'after'
);
Expand Down
37 changes: 29 additions & 8 deletions packages/api-fetch/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,14 @@ function registerMiddleware( middleware ) {
middlewares.unshift( middleware );
}

const checkStatus = ( response ) => {
if ( response.status >= 200 && response.status < 300 ) {
return response;
}

throw response;
};

const defaultFetchHandler = ( nextOptions ) => {
const { url, path, data, parse = true, ...remainingOptions } = nextOptions;
let { body, headers } = nextOptions;
Expand All @@ -71,13 +79,6 @@ const defaultFetchHandler = ( nextOptions ) => {
headers,
}
);
const checkStatus = ( response ) => {
if ( response.status >= 200 && response.status < 300 ) {
return response;
}

throw response;
};

const parseResponse = ( response ) => {
if ( parse ) {
Expand Down Expand Up @@ -148,7 +149,27 @@ function apiFetch( options ) {
return step( workingOptions, next );
};

return createRunStep( 0 )( options );
return new Promise( function( resolve, reject ) {
createRunStep( 0 )( options )
.then( resolve )
.catch( ( error ) => {
if ( error.code !== 'rest_cookie_invalid_nonce' ) {

This comment has been minimized.

Copy link
@youknowriad

youknowriad Oct 9, 2019

Contributor

Is it possible to include this behavior in the nonce middleware instead? Ideally, the raw apiFetch doesn't have any specific thing like that.

return reject( error );
}

// If the nonce is invalid, refresh it and try again.
window.fetch( apiFetch.nonceEndpoint )
.then( checkStatus )
.then( ( data ) => data.text() )
.then( ( text ) => {
apiFetch.nonceMiddleware.nonce = text;
apiFetch( options )
.then( resolve )
.catch( reject );
} )
.catch( reject );
} );
} );
}

apiFetch.use = registerMiddleware;
Expand Down
16 changes: 16 additions & 0 deletions packages/e2e-tests/plugins/nonce.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php
/**
* Plugin Name: Gutenberg Test Plugin, Nonce
* Plugin URI: https://github.com/WordPress/gutenberg
* Author: Gutenberg Team
*
* @package gutenberg-test-plugin-nonce
*/

/**
* Returns the nonce life time.
*/
function gutenberg_test_plugin_nonce_life() {
return 5;
}
add_filter( 'nonce_life', 'gutenberg_test_plugin_nonce_life' );
35 changes: 35 additions & 0 deletions packages/e2e-tests/specs/plugins/nonce.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/**
* WordPress dependencies
*/
import {
activatePlugin,
createNewPost,
deactivatePlugin,
saveDraft,
} from '@wordpress/e2e-test-utils';

describe( 'Nonce', () => {
beforeAll( async () => {
await activatePlugin( 'gutenberg-test-plugin-nonce' );
} );

afterAll( async () => {
await deactivatePlugin( 'gutenberg-test-plugin-nonce' );
} );

beforeEach( async () => {
await createNewPost();
} );

it( 'should refresh when expired', async () => {
await page.keyboard.press( 'Enter' );
// eslint-disable-next-line no-restricted-syntax
await page.waitFor( 5000 );
await page.keyboard.type( 'test' );
// `saveDraft` waits for saving to be successful, so this test would
// timeout if it's not.
await saveDraft();
// We expect a 403 status once.
expect( console ).toHaveErrored();
} );
} );

0 comments on commit be95231

Please sign in to comment.