1

TL;DR - Why is my user account not logged in during an AJAX request which is made inside wp-admin?


I have the following setup:

<?php

add_action('wp_ajax_foobar_action', 'foobar_action');
add_action('wp_ajax_nopriv_foobar_action', 'foobar_action');

function foobar_action() {
    check_ajax_referrer();

    wp_send_json((object) ['msg' => 'hello world']);
}

add_action('admin_print_scripts', function () {
    printf('<script type="text/javascript">window.custom_nonce = "%s";</script>', wp_create_nonce());
});

And in JS:

var msg = '';

// I'm using the whatwg-fetch polyfill and a polyfill for Promises.
fetch(ajaxurl, {
    method: 'POST',
    headers: {
        'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8'
    },
    body: 'action=foobar_action&_wpnonce=' + window.custom_nonce
}).then(function (res) {
    msg = res.json().msg;
});

This is all run in wp-admin. The issue is that the wp-admin page/JS script itself is loaded as a logged in user, but the AJAX request that ends up into admin-ajax.php is done as a logged out guest user. This leads to the issue of wp-admin using nopriv and check_ajax_referrer failing 100% of the time.

Why does wp-admin make AJAX requests as a guest instead of the logged in user?

Currently, the AJAX endpoint returns 403 status with -1 content, as it should in case there is a nonce mismatch. If I comment out the check_ajax_referrer() call then the AJAX request runs through successfully. After some furious console.logging I've determined that the nonce values match in the JS fetch call and the AJAX endpoint that receives it (which means the nonce is properly transferred, but a wrong guest nonce is generated during the AJAX endpoint execution.

If I remove the wp_ajax_nopriv_foobar_action then WordPress doesn't find the auth-enabled AJAX action because there seemingly is no logged in user available (results in status 200 and body 0).

Steps I've attempted to fix the issue:

  • Logged out and in again, cleared caches/cookies, used incognito windows,
  • Restarted PHP-FPM, Nginx, object cache backends, MariaDB,
  • Disabled all plugins that are not required for operation,
  • Moved the AJAX hooking code around in my plugin code, all the way to the plugin root file (e.g. myplugin/myplugin.php),
  • Used GET instead of POST.

2 Answers 2

5

After some fiddling and sparring with @t-todua I found the issue:

With the Fetch API fetch call you must manually set to send cookies with a request. After setting the credentials option properly the cookies were sent and the AJAX endpoint recognized the current user. So the JS becomes:

var msg = '';

// I'm using the whatwg-fetch polyfill and a polyfill for Promises.
fetch(ajaxurl, {
    method: 'POST',
    headers: {
        'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8'
    },
    body: 'action=foobar_action&_wpnonce=' + window.custom_nonce,
    credentials: 'same-origin'
}).then(function (res) {
    msg = res.json().msg;
});
0

I think problem could be due to the consequence:

in HTML source, firstly, this should be outputed: .......... window.custom_nonce = "%s" ... and then should be called fetch function. In foobar_action(), add this line: var_dump(is_user_logged_in()); and see, maybe it detects login (if so, then reason is in nonce. Otherwise, problem is in login)

p.s. Have you tried changing POST to GET method?

7
  • I'll try changing to a GET request if that changes anything. The generated nonce is supplied using wp_localize_script but I left that out for a leaner example code.
    – ojrask
    Commented Jan 16, 2017 at 13:07
  • Changing to GET changes nothing.
    – ojrask
    Commented Jan 16, 2017 at 13:10
  • note, you should use AJAX request in this manner: codex.wordpress.org/… you'b better show the source of FETCH function in your post. Or do a pure AJAX request like in that example.
    – T.Todua
    Commented Jan 16, 2017 at 13:12
  • 1
    Haha, you pointed me to the right direction! The fetch call must manually be set to send over cookies. Enabled that and the thing seems to work. I need to do some more testing on it. Many thanks!
    – ojrask
    Commented Jan 16, 2017 at 13:32
  • 1
    Fetch API is as pure as can be, it is implemented by browsers (caniuse.com/#search=fetch) instead of having to rely on jQuery or other libraries. ;)
    – ojrask
    Commented Jan 17, 2017 at 7:43

Your Answer

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

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