6

Though I have logged in, I cannot create a post on frontend. WordPress always return 401 Unauthorized response. I have dumped $_COOKIE to make sure logged-in cookie was set and got something like this:

array(1) {
  ["wordpress_logged_in_48f880f2beac6f39fb9ea8d9367e86d6"]=>
  string(125) "admin|1495526369|PwQIf1tAM5khs2f6LKMgf0T7fP1RwHjl9T6OWW90QfD|6d3373f1a05f2fcbfccc429035b0519a012d3224f725b82d6f253a98862b072d"
}

I have read entire REST API Handbook and many tuts:

When you log in to your dashboard, this sets up the cookies correctly for you, so plugin and theme developers need only to have a logged-in user.

but I cannot get it to work. Am I missing something or it only works on admin dashboard?

Here is my source code:

// Localized data
/* <![CDATA[ */
var appData = {"routes":{"restUrl":"\/wp-json\/wp\/v2"}};
/* ]]> */

// Post model
App.Model.Post = Backbone.Model.extend({
  urlRoot: appData.routes.restUrl + '/posts'
}

// Init
$(document).ready(function() {
  var post = new App.Model.Post({
    title: 'Posted via REST API',
    content: 'Lorem Ipsum is simply dummy text of the printing and typesetting industry.',
  });
  var xhr = post.save(null, {
    success: function(model, response, options) {
      console.log(response);
    },
    error: function(model, response, options) {
      console.log(response);
    }
  });
});

2 Answers 2

6

It looks like you're missing the nonce part, as explained in the Authentication chapter, in the REST API Handbook:

Cookie authentication is the basic authentication method included with WordPress. When you log in to your dashboard, this sets up the cookies correctly for you, so plugin and theme developers need only to have a logged-in user.

However, the REST API includes a technique called nonces to avoid CSRF issues. This prevents other sites from forcing you to perform actions without explicitly intending to do so. This requires slightly special handling for the API.

For developers using the built-in Javascript API, this is handled automatically for you. This is the recommended way to use the API for plugins and themes. Custom data models can extend wp.api.models.Base to ensure this is sent correctly for any custom requests.

For developers making manual Ajax requests, the nonce will need to be passed with each request. The API uses nonces with the action set to wp_rest. These can then be passed to the API via the _wpnonce data parameter (either POST data or in the query for GET requests), or via the X-WP-Nonce header.

Based on the Backbone JavaScript Client chapter, we can use the core wp-api REST API Backbone client library.

Here's your modified snippet in a /js/test.js script file in the current theme directory:

wp.api.loadPromise.done( function() {

  // Create a new post
  var post = new wp.api.models.Post(
      {
        title: 'Posted via REST API',
        content: 'Lorem Ipsum ... ',
        status: 'draft',  // 'draft' is default, 'publish' to publish it
    }
  );

  var xhr = post.save( null, {
     success: function(model, response, options) {
       console.log(response);
     },
     error: function(model, response, options) {
       console.log(response);
     }
   });

});

where we enqueue the wp-api client library and our test script with:

add_action( 'wp_enqueue_scripts', function()
{
    wp_enqueue_script( 'wp-api' );
    wp_enqueue_script( 'test_script', get_theme_file_uri( '/js/test.js' ), [ 'wp-api' ] );

} );

within functions.php file in the current theme.

Note that this test script just creates a new post draft, during each page load on the front-end, for a logged in user with the capability to create new posts.

4
  • 2
    Thanks, even when I'm not using the Backbone client library, I've managed to get the permissions I was lacking to check my user or upload changes, these are my humble beginnings: gist.github.com/tzkmx/d88c29f5adeda56cefef96565c197370 Mar 15, 2018 at 20:02
  • 1
    @JesúsFranco , that is such a huge help. wpApiSettings was exactly what I have been looking for to make these calls on site from a separate React script. In axios: const { root, nonce } = window.wpApiSettings; const axioswp = axios.create({ baseURL: root, headers: { "X-WP-Nonce": nonce }}); axioswp.get(root+versionString+'users/me').then((response) => { console.log(response)})
    – Jake
    May 24, 2021 at 20:49
  • 1
    glad to be of help @Jake, would you mind if I take your snippet and update my gist to keep it as a reference for use with axios? May 26, 2021 at 18:54
  • @JesúsFranco Sure thing. Thanks again.
    – Jake
    May 27, 2021 at 13:49
0

You don't need to login into your WordPress Dashboard before consuming the WordPress REST API from an external app; it's irrelevant (unless of course you want to rely on cookies as you are developing).

It is important to note that you may already have your Cross-Origin Resource Sharing (CORS) enabled on your server side unless you are consuming from the same origin or same server.

The HTTP response header 401 you had indicates an that an unauthorized request has been sent for processing and your response likely would have been:

{
  "code": "rest_cannot_edit",
  "message": "Sorry, you are not allowed to edit this post.",
  "data": {
    "status": 401
  }
}

In other words, you are either not logged in into the application you request is originating from or the very user performing the action does not have the required permission to perform such an operation.

Let's assume it is the first case (an unauthenticated user, who under normal circumstances has all rights to perform the intended action);

The obvious solution has to do with authenticating the user before performing the operation as you've guessed.

The question now is: how to authenticate a WordPress user via its in-built REST API?

The good news is: there is a range of options available for you to chose from based on your requirements and or preferences.

The snippet below demonstrates how you should go about it when using Backbone.js:

wp.api.loadPromise.done(function() {
  // Create a new post
  var post = new wp.api.models.Post({
    title: 'Posted via REST API',
    content: 'Lorem Ipsum is simply dummy text of the printing and typesetting industry.',
  });
  post.save(null, {
    success: function(model, response, options) {
      console.log(response);
    },
    error: function(model, response, options) {
      console.log(response);
    }
  });
});

Remember to enqueue wp-api in your functions.php or plugin as below:

/**
 * Either of the two can be used to enqueues in-built WP-API;
 * not both as any of them enables you achieve the same result: to enqueue wp-api.
 * The only difference between them is that one does it independently without any condition
 * while the other does so with a condition: to enqueue it as a dependency for your script.
 */
function wp_api() {
    // Use the line below to enqueue directly
    // (should your code directly reside in your functions.php or plugin).
    wp_enqueue_script( 'wp-api' );

    // Use this option instead if you want to enqueue it (wp-api)
    // as a dependency for your script (here, located in a js file) so as
    // to ensure that your script gets loaded only after wp-api does as it depends on it.
    wp_enqueue_script( 'my_script', 'path/to/my/script', array( 'wp-api' ) );
}
add_action( 'init', 'wp_api' );

... more details on using Backbone JavaScript Client with WordPress REST API here.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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