wp_verify_nonce return false despite correct parameter passed
-
20-05-2021 - |
Pergunta
I have created a form that have nonce
<form action="<?php echo get_rest_url(null, 'ilms_plugin/new_membership') ?>" method="post" class=' space-y-2 mt-2 p-2 rounded-md '>
<?php wp_nonce_field( 'new_membership','test_nonce' ); ?>
<h1 class="pt-2 text-xl">New membership</h1>
<div class='pr-2'>Membership name</div><input name="membership_name" type="text" id='shortname' class="w-full"/>
<div class='pr-2'>Description</div><textarea name="description" id="" cols="30" rows="6" class="w-full"></textarea>
<div class="pr-2">Availability</div> Day <input name="membership_period_day" type="number" id="membership_period_day" class="w-full" />
<div class="pr-2">Price</div> $ HKD <input name="membership_price" type="number" id="membership_price" class="w-full" />
<div><button class='bg-green-500 mt-2 p-2 rounded-md text-white'>Add</button></div>
</form>
In my callback in another php file, I have the following:
function new_membership($data){
global $wpdb;
$id= 0;
$nonce = $data['test_nonce'];
if ( !wp_verify_nonce($nonce, 'new_membership') ) {
var_dump("nonce rotten!");
exit; // Get out of here, the nonce is rotten!
}
if (isset ($data['membership_name']) && isset($data['description'])){
$sql = $wpdb->prepare( "INSERT INTO memberships (membership_name, membership_description, membership_period_day, membership_price) VALUES ( %s, %s, %d, %d)",
$data['membership_name'], $data['description'], $data['membership_period_day'], $data['membership_price']);
$wpdb->query($sql);
$id = $wpdb->insert_id;
}
wp_redirect(get_admin_url()."?page=iMembership-page");
exit();
}
But the wp_verify_nonce() keep returning false. I dont know what is the reason and how to fix it.
Edit to reply comment:
to Anton: the value of test_nonce is "07213d197c". On the form I set it like this
<?php wp_nonce_field( 'new_membership','test_nonce' ); ?>
to Sally CJ: yes, I call it using register_rest_route() in my custom_plugin.php
add_action('rest_api_init', function () {
register_rest_route( 'custom_plugin', 'new_membership',[
'methods' => 'POST',
'callback' => 'new_membership',
]);
}
Solução
the wp_verify_nonce() keep returning false
If you were logged-in to your (WordPress) site when you used the form, then the above is normal. Here's why so:
Your form submits to a custom REST API endpoint (at
/wp-json/ilms_plugin/new_membership
) and the default authentication method used by the REST API is cookie-based, i.e. it checks if a nonce with the action namedwp_rest
is set and that it's valid (not yet expired), and if not (not set or it's invalid/expired), then WordPress sets the current user to0
which affects all nonce-related functions including but not limited towp_verify_nonce()
.In simple words, it means functions like
wp_verify_nonce()
will return false because nonces are based on the current user, so if the user is logged-in to your site (when filling in the form), but not the REST API (when the form submission/data is processed), thenwp_verify_nonce()
will return false because the user ID becomes0
in the REST API.
Check out the REST API handbook for more details about the authentication: https://developer.wordpress.org/rest-api/using-the-rest-api/authentication/
How to fix the issue
Just add wp_nonce_field( 'wp_rest', '_wpnonce', false );
somewhere in your form, e.g. right after the opening <form>
tag: (Note that I escaped the URL using esc_url()
)
<form action="<?php echo esc_url( get_rest_url( null, 'ilms_plugin/new_membership' ) ); ?>" ...>
<?php wp_nonce_field( 'wp_rest', '_wpnonce', false ); ?>
...
Additional Notes
Instead of
get_rest_url( null, 'ilms_plugin/new_membership' )
, you could userest_url( 'ilms_plugin/new_membership' )
which usesrest_url()
and is shorter.. :)Instead of using
$wpdb->query()
, I would use$wpdb->insert()
to insert the data to the database.A REST API endpoint should always set a
permission_callback
and you should also use theargs
argument to define the parameters for the endpoint like thetest_nonce
in your case. Nonetheless, refer to Adding Custom Endpoints in the REST API handbook for further details.