phpwordpressnonce

wordpress verify_nonce not working when not logged in


I am building a custom Wordpress theme with a login form for visitors of my website. The form has a field for username, password, a hidden nonce field, and a hidden action field. On submit, the form posts to wp-admin/admin-post.php, into which I hook a custom function, to handle the login submission.

If I just take the username and password in the custom login function, and I plug them in wp_signon(), everything works great and the user can log in. However, if I try to verify the nonce using wp_verify_nonce($nonce,$action), it returns false. I created the nonce using wp_create_nonce($action). I verified that the nonce that is received by the login function in the $_POST variable, is the same as the nonce that I created, and the action that I plug into the verification function is also the same as the action I plug into the creation function.

I also have a logout function, that is accessed by a get request to mydomain/wp-admin/admin-post.php?action=action&nonce=nonce. In the custom logout function that handles this request, I check the nonce in the exact same way, but here it works fine. Is there something I need to do differently to create nonces for when a user isn't logged in yet? I thought the nonce was based on a hash of the userID and some time information, but since the user ID is 0 (not logged in) both when creating and verifying the nonce, I don't understand why this wouldn't work.

for reference, the custom login function in my functions.php. The echo statement returns the correct nonce, yet the $nonceResult is false.

function handle_customer_login() {
    $username = $_POST['username'];
    $password = $_POST['password'];
    $nonce = $_POST['_nonce'];
    $nonceResult = wp_verify_nonce($nonce,'customer_login');

    if (empty($_POST['_nonce'])) {
        echo 'error: ';
        var_dump($nonceResult);
        die();
    } else {
        echo 'nonce: '.$_POST['_nonce'];
        var_dump($nonceResult);
        die();
    }
}
add_action('admin_post_customer_login','handle_customer_login'); 
add_action('admin_post_nopriv_customer_login','handle_customer_login'); 

I create the nonce in the line of code below, and I add the $login_nonce to the wp_localize_script() function to pass it on to javascript. Then I render the login form with javascript, in which the nonce shows up just fine inside the hidden field.

$login_nonce = wp_create_nonce('customer_login');

Solution

  • I found a solution myself. Maybe this will help someone. I looked inside the function that creates the nonce and the one that validates it. Both take some variables (nonce tick, user id, session token, action) and create a hash, the last indices of which become the nonce. If you verify a nonce that's supplied by a request, the nonce is recreated based on these input variables, and if it's the same, it's valid.

    In case there is no logged in user, a fake user id is created based on filtering the userid and the action with the 'nonce_user_logged_out' filter (apply_filters( 'nonce_user_logged_out', $uid, $action );). I checked which functions are hooked into this filter step both at time of creation and of validation. It turned out, there were no functions hooked in during validation, but nonce_user_logged_out was hooked during creation. I was able to solve the problem by adding the 'nonce_user_logged_out' filter using add_filter(), before running wp_verify_nonce.