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',
]);
}
Foi útil?

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 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.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a wordpress.stackexchange
scroll top