Search code examples
wordpresswordpress-rest-api

WP Nonce "rest_cookie_invalid_nonce"


I succesffuly generate a nonce after the user is logged in, and i pass it to the client via an SSL cookie:

./functions.php

function set_nonce_cookies(){
  $nonce = wp_create_nonce( 'wp_rest');
  setcookie( 'my_nonce', $nonce );
}
add_action('wp_login', 'set_nonce_cookies');

and then the client takes the nonce form the cookies, and uses it in test calls like this one (there the variable is of course the real value from the cookie):

$.ajax({
    url: apiHost + '/wp/v2/posts',
    type: 'POST',
    data: {
        title: 'Hello World',
        content: 'This is my first post!',
        status: 'publish'
    },
    beforeSend: function (xhr) {
        xhr.setRequestHeader('X-WP-Nonce', nonce);
    },
    success: function (response) {
        console.log(response);
    },
    error: function (error) {
        console.log(error);
    }
});

but I always and only get this error (trying in incognito mode with several diff users):

{
    "code": "rest_cookie_invalid_nonce",
    "message": "Cookie check failed",
    "data": {
        "status": 403
    }
}

I cannot really figure out where is the issue. Maybe the nonce is not valid, but I generate it following the best practice. Perhaps it is generated in the wrong moment (i.e. before the user is actually logged in)?

The other cookies that exist are:

  • wordpress_test_cookie
  • wordpress_logged_in_xxxxxxxxxx
  • wp-settings-time-2

Solution

  • Ok I found the solution myself. The problem is that I was generating the nonce only once (at the user login, using the filter add_action('wp_login', 'set_nonce_cookies'); but this nonce was outdated just afer a few seconds.

    The solution is to generate generate the nonce at every page refresh, and therefore update the cookie if needed. This are the lines of code I use now in functions.php, and it works like a charm:

    function set_nonce_cookies(){
      $cookie_name = 'rest_nonce';
      $cookie_value = wp_create_nonce('wp_rest');
      $cookie_exp = 0;
      $cookie_path = "/";
      $cookie_domain = "";
      $cookie_secure = true; //<- This ensures that the SSL protocol is used
      $cookie_http_only = false; //<- If true, the cookie cannot be read with JavaScript
      setcookie($cookie_name, $cookie_value, $cookie_exp, $cookie_path, $cookie_domain, $cookie_secure, $cookie_http_only);
      }
    
    if (is_user_logged_in()) {
      set_nonce_cookies();
    }