1

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',
]);
}
3
  • Hi) what is a value of $data['test_nonce'] and how did you set it? – anton May 15 at 5:27
  • I noticed that your form is being submitted to a custom REST API endpoint, so you should submit the wp_rest nonce to that endpoint. Otherwise, the current user will always be unauthenticated even if he/she is actually logged-in to your WordPress site. And the endpoint callback should return something and not do things like exit() or redirection.. How do you call the new_membership() function? Is it directly passed to register_rest_route()? Can we see the code? – Sally CJ May 15 at 8:59
  • Dear Anton and Sally CJ, I have edited above to include my answer. Thank you – Murniyanti Malik May 15 at 17:31
1

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 named wp_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 to 0 which affects all nonce-related functions including but not limited to wp_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), then wp_verify_nonce() will return false because the user ID becomes 0 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

  1. Instead of get_rest_url( null, 'ilms_plugin/new_membership' ), you could use rest_url( 'ilms_plugin/new_membership' ) which uses rest_url() and is shorter.. :)

  2. Instead of using $wpdb->query(), I would use $wpdb->insert() to insert the data to the database.

  3. A REST API endpoint should always set a permission_callback and you should also use the args argument to define the parameters for the endpoint like the test_nonce in your case. Nonetheless, refer to Adding Custom Endpoints in the REST API handbook for further details.

1

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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