最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

ajax - wp_create_nonce() in REST API makes user->ID zero

programmeradmin0浏览0评论

I have trouble with wp_create_nonce() and wp_verify_nonce().

The root of my problem is both the way nonces are created and the place where I generate nonces.

Looking at the codes of wp_create_nonce() and wp_verify_nonce(), I see that nonces are a hash value created from multiple factors including $uid, which is a user id of current user.

$user = wp_get_current_user();
$uid = (int) $user->ID;
if ( ! $uid ) {
    /** This filter is documented in wp-includes/pluggable.php */
   $uid = apply_filters( 'nonce_user_logged_out', $uid, $action );

}
$token = wp_get_session_token();
$i = wp_nonce_tick();
return substr( wp_hash( $i . '|' . $action . '|' . $uid . '|' . $token, 'nonce' ), -12, 10 ); 

I create the nonce under REST API and verify the nonce in normal page. However, I also find that $uid becomes 0 in REST API (dunno why, but somewhere I saw this and tested it myself. Yes, there's no user id inside REST). So, the $uid in my REST function and the $uid in my page become different.

[1] I create _wpnonce in REST API. Below is a pseudo-code for my function in REST.

add_action( 'rest_api_init', function() {
    register_rest_route( 'view/', '/customers/', array(
        'methods' => 'POST',
        'callback' => 'fnc_view_customer_in_table_format'
    ));
});

if( !function_exists( 'fnc_view_customer_in_table_format' ) ) {
    function fnc_view_customer_in_table_format(WP_REST_Request $request ) {

    //*** Code omitted on purpose ***

    $data .= '<form action="'. get_the_permalink( $post->ID ) .'" id="_form-view" name="_form-view" method="post">';
    $data .= '<input type="hidden" id="_wpnonce" name="_wpnonce" value="'. wp_create_nonce( 'view_post-'. $post->ID ) .'" />';
    $data .= '<input type="hidden" id="_post_id" name="_post_id" value="'. $post->ID. '" />';
    $data .= '<input type="submit" class="btn btn-sm btn-link" value="'. get_the_title( $post->ID ) .'">';
    $data .= '</form>';

    //*** Code omitted on purpose ***

    $result = array( 'msg' => $data, 'error' => false );
}

[2] Above $data is returned as a response and is echoed in normal page. And I verify the nonce here. This is a normal wordpress page.

<?php
get_header();

$wpnonce = $_REQUEST['_wpnonce'];
$post_id = $_REQUEST['_post_id'];
if( !wp_verify_nonce( $wpnonce, 'view_post-'. $post_id ) ) {
    die( 'Security check' );
}

?>

//*** Omitted: HTML Codes ***

<?php
get_footer();

So, I presume that the only solution is let Wordpress REST recognize current_user. How do I achieve this, or am I thinking the wrong way?

** EDIT ** As a workaround, I changed my wp_verify_nonce process as below.

// nonce is created in REST API, which doesn't have UID
// In order to replicate the same, set UID to 0 here
$uid = (int) wp_get_current_user()->ID;
wp_set_current_user(0);

// Nonce check
if( !wp_verify_nonce( $wpnonce, 'view_post-'. $post_id ) ) {
die( 'Security check' );
}

// Now get the current user back in place
wp_set_current_user( $uid );

Anyways, this doesn't look beautiful. What would be a better practice to solve my problem?

I have trouble with wp_create_nonce() and wp_verify_nonce().

The root of my problem is both the way nonces are created and the place where I generate nonces.

Looking at the codes of wp_create_nonce() and wp_verify_nonce(), I see that nonces are a hash value created from multiple factors including $uid, which is a user id of current user.

$user = wp_get_current_user();
$uid = (int) $user->ID;
if ( ! $uid ) {
    /** This filter is documented in wp-includes/pluggable.php */
   $uid = apply_filters( 'nonce_user_logged_out', $uid, $action );

}
$token = wp_get_session_token();
$i = wp_nonce_tick();
return substr( wp_hash( $i . '|' . $action . '|' . $uid . '|' . $token, 'nonce' ), -12, 10 ); 

I create the nonce under REST API and verify the nonce in normal page. However, I also find that $uid becomes 0 in REST API (dunno why, but somewhere I saw this and tested it myself. Yes, there's no user id inside REST). So, the $uid in my REST function and the $uid in my page become different.

[1] I create _wpnonce in REST API. Below is a pseudo-code for my function in REST.

add_action( 'rest_api_init', function() {
    register_rest_route( 'view/', '/customers/', array(
        'methods' => 'POST',
        'callback' => 'fnc_view_customer_in_table_format'
    ));
});

if( !function_exists( 'fnc_view_customer_in_table_format' ) ) {
    function fnc_view_customer_in_table_format(WP_REST_Request $request ) {

    //*** Code omitted on purpose ***

    $data .= '<form action="'. get_the_permalink( $post->ID ) .'" id="_form-view" name="_form-view" method="post">';
    $data .= '<input type="hidden" id="_wpnonce" name="_wpnonce" value="'. wp_create_nonce( 'view_post-'. $post->ID ) .'" />';
    $data .= '<input type="hidden" id="_post_id" name="_post_id" value="'. $post->ID. '" />';
    $data .= '<input type="submit" class="btn btn-sm btn-link" value="'. get_the_title( $post->ID ) .'">';
    $data .= '</form>';

    //*** Code omitted on purpose ***

    $result = array( 'msg' => $data, 'error' => false );
}

[2] Above $data is returned as a response and is echoed in normal page. And I verify the nonce here. This is a normal wordpress page.

<?php
get_header();

$wpnonce = $_REQUEST['_wpnonce'];
$post_id = $_REQUEST['_post_id'];
if( !wp_verify_nonce( $wpnonce, 'view_post-'. $post_id ) ) {
    die( 'Security check' );
}

?>

//*** Omitted: HTML Codes ***

<?php
get_footer();

So, I presume that the only solution is let Wordpress REST recognize current_user. How do I achieve this, or am I thinking the wrong way?

** EDIT ** As a workaround, I changed my wp_verify_nonce process as below.

// nonce is created in REST API, which doesn't have UID
// In order to replicate the same, set UID to 0 here
$uid = (int) wp_get_current_user()->ID;
wp_set_current_user(0);

// Nonce check
if( !wp_verify_nonce( $wpnonce, 'view_post-'. $post_id ) ) {
die( 'Security check' );
}

// Now get the current user back in place
wp_set_current_user( $uid );

Anyways, this doesn't look beautiful. What would be a better practice to solve my problem?

Share Improve this question edited May 12, 2017 at 15:32 Dongsan asked May 11, 2017 at 9:26 DongsanDongsan 851 silver badge9 bronze badges 2
  • Could elaborate on where your code [2] lives? Is it hooked onto an action? If so, which? (It doesn't appear as you're using WordPress' REST API however). – Stephen Harris Commented May 12, 2017 at 11:25
  • I have a page (let's say page A) that calls the REST function fnc_view_customer_in_table_format() and echoes the response, which is basically a HTML code for <form></form>. Once the <form> is submitted, page [2] is called and the nonce is verified in page [2]. – Dongsan Commented May 12, 2017 at 15:37
Add a comment  | 

1 Answer 1

Reset to default 1

I know it pasts a long time of this question.

But I had the same issue and it was difficult for me to find some documentation about it. I had the same problem, for some reason wp_create_nonce and wp_verify_nonce they bot retrieve different hash. So I notice the problem was the $User->ID or $uid that these two function uses to hash the nonce.

I just trying to create the nonce inside the script but for some reason the uid become 0, and what I did, is to create the nonce before and outside the script block and print the value inside the script.

First it was like this (WRONG):

  var action     = 'submit_user_review';                  
  var ajax_nonce = '<?php echo wp_create_nonce('security'); ?>';

  jQuery.ajax({
       url: my_ajax_object.ajax_url,
       type: 'POST',
       data: {
           action: action,
           listing_id: rest_id,
           review: comment,
           rate: rate,
           security_review: ajax_nonce,
       },
       beforeSend: function() {
           // Show full page LoadingOverlay
           jQuery.LoadingOverlay("show");
      },            

and after like this (GOOD):

  <?php //Set Your Nonce
      $ajax_nonce_review = wp_create_nonce( "submit-user-review" );
  ?>
  <script>                  
  var action = 'submit_user_review';

  jQuery.ajax({
       url: my_ajax_object.ajax_url,
       type: 'POST',
       data: {
           action: action,
           listing_id: rest_id,
           review: comment,
           rate: rate,
           security_review: '<?php echo $ajax_nonce_review; ?>',
       },
       beforeSend: function() {
           // Show full page LoadingOverlay
           jQuery.LoadingOverlay("show");
      }, 

And that fix my problem, literally spend one day trying to figure it out this.

I hope it helps somebody else.

发布评论

评论列表(0)

  1. 暂无评论