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

Question

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?

0
, Dongsan 2 years 2019-12-28T21:29:00-05:00 0 Answers 116 views 0

Leave an answer

Browse
Browse