Following a link comment here which led me to these docs, I’ve tried to set up a nonce for user authentication.
I added:
wp_localize_script( 'wp-api', 'wpApiSettings', array(
'root' => esc_url_raw( rest_url() ),
'nonce' => wp_create_nonce( 'wp_rest' )
) );
to the if statement checking the user is logged in and able to do the things that will be asked in the REST call.
I also added:
beforeSend: function ( xhr ) {
xhr.setRequestHeader( 'X-WP-Nonce', wpApiSettings.nonce );
},
to my api call jQuery class.
I got an error telling me wpApiSettings does not exist. What have I done wrong?
First off..
(This info is for other readers who don’t know yet about this.) There are two ways of how you can authenticate your REST API request:
You can read more on the official REST API Handbook here, and this answer is for the standard cookie authentication, where the nonce should be sent either via a GET/POST
query parameter named _wpnonce
or a (custom HTTP) header named X-WP-Nonce
.
Options for sending the cookie nonce
-
Easy option: Append _wpnonce
to the URL of the REST API endpoint. This works with GET
, POST
, etc. requests including JSON payloads.
jQuery.ajax({
method: 'POST',
// _wpnonce as a GET/$_GET query parameter
url: '/path/to/endpoint?_wpnonce=<nonce>',
data: { foo: 'bar', baz: 1 },
dataType: 'json',
success: function ( data ) {
console.log( data );
},
});
-
Or add _wpnonce
to the request body.
jQuery.ajax({
method: 'POST',
url: '/path/to/endpoint',
// _wpnonce as a POST/$_POST query parameter
// but can be GET; see the `method` above which defaults to GET when not specified
data: { foo: 'bar', baz: 1, _wpnonce: '<nonce>' },
dataType: 'json',
success: function ( data ) {
console.log( data );
},
});
-
Or particularly when sending JSON payload (just like the example code in this question): Add X-WP-Nonce
to the request headers. This option also works well with GET
, POST
, etc. requests.
jQuery.ajax({
method: 'POST',
url: '/path/to/endpoint',
data: JSON.stringify( { foo: 'bar', baz: 1 } ), // sending a JSON-encoded string
contentType: 'application/json; charset=utf-8', // and a JSON Content-Type header
// Send the nonce as part of the headers.
beforeSend: function ( xhr ) {
xhr.setRequestHeader( 'X-WP-Nonce', '<nonce>' );
},
dataType: 'json',
success: function ( data ) {
console.log( data );
},
});
Now an example:
PHP part: (in your theme functions file or your plugin file)
// Register a dummy REST API endpoint..
add_action( 'rest_api_init', 'my_register_rest_routes' );
function my_register_rest_routes() {
register_rest_route( 'my-plugin/v1', '/foo', [
'methods' => 'POST',
'callback' => function ( $request ) {
return [
$request->get_params(),
'Is user logged-in: ' . ( is_user_logged_in() ? 'Yes' : 'NO' ),
'Can user publish_posts: ' . ( current_user_can( 'publish_posts' ) ? 'Yes' : 'NO' )
];
},
] );
}
add_action( 'wp_enqueue_scripts', 'my_enqueue_scripts' );
function my_enqueue_scripts() {
// Enqueue the script which makes the AJAX call to /wp-json/my-plugin/v1/foo.
wp_enqueue_script( 'my-script', '/path/to/my-script.js', [ 'jquery' ] );
// Register custom variables for the AJAX script.
wp_localize_script( 'my-script', 'myScriptVars', [
'root' => esc_url_raw( rest_url() ),
'nonce' => wp_create_nonce( 'wp_rest' ),
] );
}
Notes:
-
Remember that the first parameter for wp_enqueue_script()
— my-script
in the above example — is exactly the same as the first parameter for wp_localize_script()
. And that parameter is the script handle which is a unique slug as an identifier for the script you are enqueueing or localizing.
-
If those parameters don’t match, then the script wouldn’t be localized, and the JS object — myScriptVars
in the above example — would be an undefined
which could result in the error such as the one mentioned in the question (“wpApiSettings does not exist“). 🙂
JS part: (in my-script.js
or whatever is the file name…)
Here, we add _wpnonce
to the request body.
jQuery.ajax({
method: 'POST',
url: myScriptVars.root + 'my-plugin/v1/foo',
data: { foo: 'bar', baz: 1, _wpnonce: myScriptVars.nonce },
dataType: 'json',
success: function ( data ) {
console.log( data );
},
});
Notes:
The above and the other JS code in this answer uses jQuery’s ajax()
.