2

Building my first WordPress in over a decade... using Timber for MVC. Vue on the frontend, and axios for ajax posts. All hosted on the same domain, so I can use cookies for authentication.

I am enqueing axios - and setting up a nonce as a local variable which I pick up in my Vue and use to set the header when posting through axios.

I have a number of custom API routes registered eg:

add_action( 'rest_api_init', function () {
    register_rest_route( 'rw-user/v1', '/log-in', array(
        'methods' => 'POST',
        'callback' => 'ajax_login',
    ));
    register_rest_route('rw-user/v1','/log-out', array(
        'methods' => 'POST',
        "callback" => 'ajax_logout'
    ));
     // .... truncated ....
});

I'm puzzled because I can hit these endpoints with and without a NONCE (amuses me because I'm British).

If I pass the nonce header - it checks the nonce and runs the function if ok, rejects if not ok. If I don't pass the nonce header, it doesn't check the nonce, yet still runs the function...

I read in the documentation:

https://developer.wordpress.org/rest-api/using-the-rest-api/authentication/

"Note that you do not need to verify that the nonce is valid inside your custom end point. This is automatically done for you in"

So, if the endpoint is already doing the verification, surely it should fail if no nonce is present?

Does this mean that rest endpoints do not automatically check the validity of the nonce, and I should check the validity within the function myself?

Also - some pages heavily uses AJAX to interact with these custom end points. With each AJAX request, the nonce becomes invalid, breaking future AJAX requests (I'm using POST).

I assumed that I need to generate a new nonce in the function that receives the AJAX request and returns this new nonce to the browser so I can post the new nonce on the header of the next AJAX request.

$new_nonce = wp_create_nonce( 'new_nonce' );
wp_send_json_success(array('nonce'=>$new_nonce ));

I can see the new nonce returned in my console... I used it to update the header in my next request - but the nonce fails despite being new?

Any advice on the correct way to use NONCES would be much appreciated. Thanks!

2

For restricting access to your REST API endpoint, you can use the permission_callback parameter like so:

register_rest_route( 'rw-user/v1', '/log-out', array(
    'methods'             => 'POST',
    'callback'            => 'ajax_logout',
    'permission_callback' => function () {
        return current_user_can( 'read' );
    },
) );

And that will require the current user to be logged into WordPress and also the REST API in order to access the endpoint at example.com/wp-json/rw-user/v1/log-out.

So, if the endpoint is already doing the verification, surely it should fail if no nonce is present?

Yes. However,

If I don't pass the nonce header, it doesn't check the nonce, yet still runs the function...

That's because the endpoint's callback is always called once the permissions callback returns a true — and note that, in WordPress 5.5 and later, you should always set the callback — you can use __return_true to allow all access to the endpoint. E.g.

register_rest_route( 'rw-user/v1', '/log-in', array(
    'methods'             => 'POST',
    'callback'            => 'ajax_login',
    'permission_callback' => '__return_true',
) );

I can see the new nonce returned in my console... I used it to update the header in my next request - but the nonce fails despite being new?

It's probably because you're not sending the correct nonce.

  • The nonce action for authenticating the current user is wp_rest, so you should use wp_create_nonce( 'wp_rest' ) when generating the (new) nonce.

  • And be sure to send the nonce via either the X-WP-Nonce header (preferred) or the _wpnonce data parameter (which is not bad, but not very reliable).

So regarding that "do not need to verify", it's only true for the wp_rest nonce (i.e. the default cookie-based authentication). All other nonces, should there be any, need to be verified manually.

1
  • 1
    Thanks Sally :) So, by using a permission_callback the nonce becomes "required" on the rest API. The permission callback is also an additional check to the cookie to ensure the user is logged in. On the login route, I set the permission_callback to 'permission_callback' => '__return_true' (as they're logging in - they won't have permissions), and set the can read / write on all other calls. I shall go and refactor now. Thank you very much for this answer - its most helpful. It means I can make some progress. – Rob Nov 2 '20 at 21:27

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.