You won’t want just anybody to be able to access your endpoints if you’re building a single-page application within the admin area. Although the documentation is excellent, I wanted to provide a little “how-to” on this page.

Thus, here’s how to guarantee the security of your endpoints.

Pre-requisites

  • You need to know how to create a WordPress rest endpoint
  • You have a WordPress installation setup

The How-To

First off, this is what your register_rest_route option would look like at its simplest:

$options = [
  'methods' => 'GET',
  'callback' => function() {
    return 'This is a protected route. Hi there!';
  }
];

The “methods” param is the http protocol

The “callback” param is the function where you return data or do an action.

However, our callback is not protected. Anybody can access that route, and take any action and retrieve any data that our route allows.

So how do we protect it?

Just add a permission_callback parameter. Return true or false to whether our user can access that callback.

$options = [
  'methods' => 'GET',
  'callback' => function() {
    return 'This is a protected route. Hi there!';
  },
  'permission_callback' => function() {
    return false;
  }
];

At the moment, as the permission’s callback returns false, no one can access it.

Permitting certain users

WordPress has a function called, current_user_can. It’s a global function that takes globally accessible information about the current user.

$options = [
  /*....*/
  'permission_callback' => function() {
    return current_user_can('edit_others_posts');
  }
];

Most importantly, it takes the capability that you require for that route. So if say, you only wanted users who can activate_plugins to access that route, but the current user can only edit_others_posts; no access for them!

Tell WordPress who’s the current user

It’s all good to protect routes by capabilities, but WordPress won’t know who the current user is without receiving two pieces of information.

  1. Cookies. WordPress sets cookies when you’re in the administration area.
  2. You need to send back a nonce. It’s not a one-time nonce, but that’s another story. This needs to go in the header as X-WP-Nonce.

If you use the Fetch API, your request will look like so:

fetch('/wp-json/myplugin/v1/author/hi', {
    credentials: 'include',
    headers: {
      'content-type': 'application/json',
      'X-WP-Nonce': wpApiSettings.nonce
    }
})

Sending both a cookie and a separate nonce is good for security. However, cookies are not sent automatically with the fetch API, or with jQuery Ajax, or even with Axios. You have to include credentials as I’ve done above.

Finally. How do we get the nonce?

Add a wp_rest nonce to the webpage. I normally add it as a localized script on the page after my custom javascript file.

add_action('admin_init', function() {
  wp_localize_script('custom-js-file', 'wpApiSettings', array(
    'root' => esc_url_raw(rest_url()),
    'nonce' => wp_create_nonce('wp_rest')
  ));
});

That nonce must be generated the WordPress way using wp_create_nonce.

Everything put together

<?php

add_action('rest_api_init', function() {
  register_rest_route('myplugin/v1', '/author/hi', [
    /**
     * Http method can be also PUT, DELETE, POST
     */
    'methods' => 'GET',
    
    /**
     * Response to the user
     */
    'callback' => function() {
      return 'Hello world!';
    },
    
    /**
     * This is where the authentication happens
     */
    'permission_callback' => function() {
      return current_user_can('edit_others_posts');
    }
  ]);
});
<?php

wp_register_script(
    'custom-js-file', 
    plugin_dir_url(dirname(__FILE__)) . '/custom.js', 
    [], 
    null, 
    false 
);

wp_enqueue_script('custom-js-file');

add_action('admin_init', function() {
  wp_localize_script('custom-js-file', 'wpApiSettings', array(
    'root' => esc_url_raw(rest_url()),
    'nonce' => wp_create_nonce('wp_rest')
  ));
});

And that’s that!

Your routes are protected. It’s barely much code at all.

error: Content is protected !!