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 |1 Answer
Reset to default 1I 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.
[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:25fnc_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