Chris J. Zähller
Chris J. Zähller

Reputation: 133

Troubleshooting Wordpress nonce in AJAX call

I'm using AJAX to check body class and conditionally fire one of 2 actions. If the Google Speech API is supported, a voice-search capable form loads. If it isn't, a regular search form loads.

The check_ajax_referer() function is coming up 403, so there's something wrong with my nonce.

The website is mercury.photo. Code is simplified for clarity; I already know it works if I don't include the nonce check.

In my script, I have:

//-------------------------------------------------*/
// Speech Recognition
//-------------------------------------------------*/

jQuery(document).ready(function ($) {
  $(function () {
    // Add body class if browser supports Google Voice API
    if (Modernizr.speechrecognition) {
      $("body").addClass("voice-searchable");
    }
    var ajaxData;
    if ($("body").hasClass("voice-searchable")) {
      ajaxData = { "action": "my_voice" }
    } else {
      ajaxData = { "action": "my_novoice" }
    };
    xhr = new XMLHttpRequest();
    // Use a variable so we can callback 'done' and 'fail'; see https://medium.com/coding-design/writing-better-ajax-8ee4a7fb95f
    var ajaxCall = $.ajax({
      type: "POST",
      url: myVoiceSearch.ajaxurl,
      dataType: "html",
      data: ajaxData,
      security: myVoiceSearch.ajax_nonce
    });
    // 'success' & 'error' promises are deprecated; use 'done' and 'fail' instead
    ajaxCall.done(function (data) {
      $(".breadcrumb_search").html(data);
    });
    ajaxCall.fail(function (data, xhr, ajaxOptions, thrownError) {
      var errorMsg = "Oh, bother. Your AJAX request failed with a status code of " + xhr.responseText + ".";
      console.log( errorMsg );
    });
  });
});
(window, jQuery, window.Window_Ready);
// More scripts to load Google Voice API

And in functions.php, I enqueue my scripts and add the actions:

/*
 * Ajax scripts for speech recognition
 */

add_action( 'wp_print_scripts', 'my_search_ajax_enqueue' );
function my_search_ajax_enqueue() {
  if ( ! is_admin() ) { // Load the script only on the front end
    $protocol = isset( $_SERVER['HTTPS'] ) ? 'https://' : 'http://';
    wp_register_script( "my-search-ajax-script", get_stylesheet_directory_uri() . '/js/speech-input.js', array( 'jquery' ) );

    $params = array( 'ajaxurl' => admin_url( 'admin-ajax.php', $protocol), 'ajax_nonce' => wp_create_nonce( 'my-create-voice-search-nonce' ), );

    wp_localize_script( 'my-search-ajax-script', 'myVoiceSearch', $params );

    wp_enqueue_script( 'my-search-ajax-script' );
  };
}

/**
 *
 * Generate custom search form
 *
 **/

function my_voice() {
  // This check fails
  check_ajax_referer( 'my-create-voice-search-nonce', 'security' );
  $form = 'A most excellent form, m’lord.';
  echo $form;
  wp_die();
}

add_action('wp_ajax_my_voice', 'my_voice');
add_action('wp_ajax_nopriv_my_voice', 'my_voice');

function my_novoice() {
  // check_ajax_referer( 'my-create-voice-search-nonce', 'security' );
  $form = 'Lame form, dude!';
  echo $form;
  wp_die();
}


add_action('wp_ajax_my_novoice', 'my_novoice');
add_action('wp_ajax_nopriv_my_novoice', 'my_novoice');
  1. How can I log whether the nonce is being seen in the AJAX call?
  2. What might be preventing the nonce from either being set (in my PHP), or received (in my AJAX)?

EDIT: Did some more reading, and it appears the main function of WP nonces is to prevent CSRF, and as such, is really only useful for logged-in users. So, more questions:

  1. Should I even bother with nonces on this AJAX call?
  2. Is there a better way to handle verifiying the call's authenticity?

EDIT: Further reading confirms that I shouldn't even be doing this, since it's a front-end form. Closing this question, but feel free to comment/answer if you know what was wrong with my code — I always appreciate the opportunity to learn!

EDIT: Answer given and accepted.

Upvotes: 0

Views: 3022

Answers (1)

Omar Tanti
Omar Tanti

Reputation: 1458

All the variables to be accessible for the wp_ajax callback functions need to be set in the data argument of the jquery ajax request. Therefore the nonce value needs to be included in your ajaxData variable as follows:

ajaxData = { "action": "my_voice", "security": myVoiceSearch.ajax_nonce}

Upvotes: 1

Related Questions