WP REST API nonce authentication

Nonce API REST validation with the WordPress 4.4 infrastructure.

It’s part of Core

The WordPrss REST API — WordPress Plugins”) is part of WordPress core since version 4.4. Without installing and activating the WP REST API plugin — WordPress Plugins”) what I will get, as a developer, is merely an infrastructure.
This means no endpoints, no authentication handling and so on.
Still this is a mighty possibility to tap in and with an implementation that thinks ahead it’s worth putting some effort into code that harnesses this power now.
I’ve been recently developing the “I’d like this” plugin for research and experimentation purposes and ran into the need for a nonce validation full speed ahead.

Nonces - the wrong way

Since the plugin will rely on an AJAX call to trigger the creation of a comment related to a post I’ve baked some simple authentication method in the plugin to add a measure of security to it: requests should come from the site front-end only and WordPress relies on the nonce mechanic to deal with that.
In very simple terms I’m localizing a JavaScript array on the site front-end that contains a nonce specific to the action

/**
 * Returns an array containing the data to be localized.
 *
 * @return array
 */
public function get_data()
{
    $nonce = wp_create_nonce('button-click');
    return array(
        'endpoints' => array(
            'domain' => home_url(),
            'button_click' => array(
                'url' => rest_get_url_prefix() . '/idlikethis/v1/button-click/',
                'nonce' => $nonce,
            )
        )
    );
}

and when handling a click request I was trying to verify the nonce with this method, auth is where the generated nonce is stored

/**
 * Verifies an action is authorized.
 *
 * @param WP_REST_Request $request The request representation.
 * @param string $action The action the auth refers to.
 * @return bool
 */
public function verify_auth(WP_REST_Request $request, $action)
{
    if (!is_string($action)) {
        throw new InvalidArgumentException('Action must be a string');
    }

    $this->action = $action;

    $auth = $request->get_param('auth');
    if (empty($auth)) {
        return false;
    }

    return wp_verify_nonce($auth, $action);
}

And this was passing some times and failing others. The nonce verification system relies on the current user id to work and having the code above pass would require to sync the nonce verification to the current user ID; by default the user ID for any REST request will be 0: the nonce verification will pass any time the request is made from the front-end when no user is logged in and fail if the user is logged in (user ID not 0).

Nonces - the right way

The nonce system the REST API infrastructure implements relies on the user authentication cookie to work but will require an header to be set for the request.
The localized data provider I’ve not modified. That same nonce value will have to be assigned to the X-WP-Nonce header of the request.
In Backbone terms this means that the model implementation will modify the sync method implementation to set the header before the request is sent; in even more specific terms for the “I’d like this” project the code of the vote caster class is the following

var data = require('localized-data'),
    Backbone = require('backbone');

module.exports = Backbone.Model.extend({

    url: data.endpoints.button_click.url,

    sync: function () {

        Backbone.sync('create', this, {
            beforeSend: function (xhr) {
                xhr.setRequestHeader('X-WP-NONCE', data.endpoints.nonce);
            },
        });

    },

});

Nothing incredible but still a useful thing to know to work with the REST API infrastructure now.

Read the documentation

And all of this can be found in the REST API documentation in greater detail.