JGinDC
JGinDC

Reputation: 91

WP AJAX: nonce works when logged out, but not when logged in

I have a WordPress site that I'm configuring to use jQuery/AJAX to query and load posts inside a div box.

Here's what I have in functions.php (massively simplified, naturally):

function mysite_enqueue_scripts() {
  wp_register_script( 'mysite_front' , get_bloginfo( 'template_url' ) . '/js/front.js' , array( 'jquery' ) );
  wp_enqueue_script( 'mysite_front' ); //loads the front-side jquery
  wp_localize_script( 'mysite_front' , 'AJAX' , array( 
    'ajaxurl' => admin_url( 'admin-ajax.php' ) ,
    'nonce' => wp_create_nonce( 'mysite-ajax-nonce' )
  ) ); //localizes the script
}
add_action( 'wp_enqueue_scripts' , 'mysite_enqueue_scripts' , 100 );
add_action( 'wp_ajax_nopriv_load_items' , 'mysite_ajax_load_items' );
add_action( 'wp_ajax_load_items' , 'mysite_ajax_load_items' );

function mysite_ajax_load_items() {
  if( !check_ajax_referer( 'mysite-ajax-nonce' , 'nonce' , false ) ) {
    wp_send_json( array( 'error' => 'nonce error' ) );
    return;
  } else {
    [[[all the WP query stuff goes here and creates an $html string]]]
    wp_send_json( array( 'html' => $html ) );
  }
}

The jQuery front.js:

jQuery( document ).ready( function($) {
  $( '#mysite-load' ).click( function() {
    var postData = {
      action : 'load_items' ,
      nonce : AJAX.nonce
    };
    jQuery.post(
      AJAX.ajaxurl , postData , function( response ) {
        if( typeof( response['html'] ) !== 'undefined' ) {
          $( '#mysite-load' ).html( response['html'] );
        } else if( typeof( response['error'] ) !== 'undefined' ) {
          $( '#mysite-load' ).html( 'Error: ' + response['error'] );
        }
      }
    );
  });
});

The HTML:

<div id="mysite-load">click</div>

When I'm not logged into the wp-admin for the site and load this page, everything works perfectly.

But when I'm logged into the wp-admin for the site and load this page, it returns the 'nonce error' error rather than the HTML it should be loading.

The problem isn't browser-specific; tried in both Safari and Chrome and received the same error. I also tried using wp_verify_nonce() instead of check_ajax_referer(), and received the same result.

Any idea why this is occurring?

Upvotes: 3

Views: 1892

Answers (1)

Mike Pollitt
Mike Pollitt

Reputation: 2007

I had exactly this problem. I found that the issue was that nonces are handled in a specific way for API AJAX requests. In my case, this was using custom endpoints, but I imagine it would be the same for any API call.

The relevant documentation is here: https://developer.wordpress.org/rest-api/using-the-rest-api/authentication/. The specific paragraph of interest is under Cookie Authentication:

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.

In practice I found this means you must at a minimum use a nonce with the action set to "wp-rest" in order to ensure that the logged-in user is correctly loaded in the API request context. Otherwise you are generating your custom nonce with a logged-in user, but trying to verify it with an anonymous user (which is the default for API calls unless you use this nonce).

$nonce = wp_create_nonce( 'wp_rest' );

You also then need to ensure the nonce you generate with this action is returned via the X-WP-Nonce header.

$.ajax({
  type: 'POST',
  url: your-api-url-here,
  contentType: 'application/json; charset=UTF-8',
  beforeSend: function(jqXhr) {
    jqXhr.setRequestHeader('X-WP-Nonce', nonce)
  },
  data: your-data-here
})

(for clarity I've left out the script localisation parts for transferring the nonce into the javascript context, but you have those in your question so I'm assuming that's all fine)

Once you've done this, your AJAX calls will no longer be anonymous (if you are logged in), and your other nonce will now verify correctly.

Upvotes: 2

Related Questions