Can’t GET draft posts via REST API from headless frontend

I have a headless install of WordPress on v5.4.0. The frontend can’t GET any post that is in draft status, though when accessing the API url directly the data is returned without an issue. I’m assuming this is cookie/auth related. Note, I’m dynamically grabbing the nonce values from the backend, showing full strings here for the sake of argument. Here’s my setup and screenshots of the responses:

Request URL: http://wordpress.test/wp-json/wp/v2/pages/19060?wpnonce=23c01b3b12&_embed=true

Called on the frontend using isomorphic-unfetch (also tried with axios) like so:

fetch("http://wordpress.test/wp-json/wp/v2/pages/19060?wpnonce=23c01b3b12&_embed=true", {
  credentials: "include"
})

Here’s the request/response from the frontend, which is not working (this does work for all published content):

With this body:

{
  "code":"rest_forbidden",
  "message":"Sorry, you are not allowed to do that.",
  "data":{
  "status":401
  }
}

When trying to access the data directly, I see two different responses depending on the wpnonce query parameter underscore:

This works (using _wpnonce) returning the proper JSON data:

http://wordpress.test/wp-json/wp/v2/pages/19060?_wpnonce=23c01b3b12&_embed=true

Details:

This does not (using wpnonce):

http://wordpress.test/wp-json/wp/v2/pages/19060?wpnonce=23c01b3b12&_embed=true

Details:

Returns the same response error that the frontend does:

{
  "code":"rest_forbidden",
  "message":"Sorry, you are not allowed to do that.",
  "data":{
  "status":401
  }
}

EDIT:

I’ve also tried the X-WP-Nonce header mentioned here (https://developer.wordpress.org/rest-api/using-the-rest-api/authentication/) and it doesn’t seem to be recognized:

fetch(postUrl, {
  credentials: "include",
  headers: {
    "X-WP-Nonce": wpnonce
  }
})

{"code":"rest_cookie_invalid_nonce","message":"Cookie nonce is invalid","data":{"status":403}}

EDIT 2:

On a clean install of WP and a new barebones frontend (html + jquery) I’m seeing the same issue. However, I find that the nonce is valid when generating the preview link, but invalid when WP tries to verify using rest_cookie_check_errors. How can the same nonce with the same action (wp_rest) fail verification?:

I’m seeing that wp_get_session_token() is failing because there’s no cookie found on REST requests. (https://core.trac.wordpress.org/browser/tags/5.4/src/wp-includes/pluggable.php#L2140) Isn’t the point of the nonce to not require cookies? Could it be an issue with cross-domain cookie paths?

I am sending credentials in the XHR. Here’s my frontend test code:

// GET page with Nonce header
$.ajax({
  url: postUrl,
  method: "GET",
  xhrFields: {
    withCredentials: true
  },
  beforeSend: function(xhr) {
    xhr.setRequestHeader("X-WP-Nonce", wpnonce);
    xhr.setRequestHeader("Content-Type", "application/json");
  }
})

Two questions:

  1. Mainly, how can I get draft post data using nonce values from the frontend?
  2. Why in the world would the _wpnonce / wpnonce query vars show different results on the frontend and backend?

1 Answer
1

Ok, so technically I didn’t solve the issue that prevented me from accessing posts in draft status from an external domain where the frontend is hosted using only nonce values. My guess is that the logged_in cookie was not set/could not be read on the frontend side to verify the nonce value during an external AJAX request.

That being said, I installed the JWT Authentication for the WP REST API plugin (https://github.com/Tmeister/wp-api-jwt-auth) and now require admins to login once on the frontend preview page to set the token in local storage.

Leave a Comment