Identical wp_rest nonce returned from rest_api

Despite you already figured it out or found a solution, since this is a follow-up to the other question, I thought it’d be benefecial to post this answer, so hopefully it helps you. =)

WordPress uses the native setcookie() function in PHP for setting the authentication cookies, and $_COOKIE for reading the cookies values. But the thing is, PHP doesn’t immediately update the values in the $_COOKIE array and instead, it’ll only be updated once the page is reloaded.

So because WordPress nonces rely upon the current user and the authentication cookies, you’ll need to do these in order to get the correct nonce on the very same page/request — i.e. without page reloads:

  1. Immediately update the $_COOKIE array once you’ve called wp_signon() (which calls wp_set_auth_cookie()). And you can do that using the set_logged_in_cookie hook.

  2. And then call wp_set_current_user() after logging in the user — if you don’t call it, the generated nonce will not use the current user and functions like is_user_logged_in() and current_user_can() would also return false.

Working Example

function wpse_377570( $logged_in_cookie ) {
    $_COOKIE[ LOGGED_IN_COOKIE ] = $logged_in_cookie;
}
add_action( 'set_logged_in_cookie', 'wpse_377570' );

function ajax_login( $request ) {
    // Logs the user in and set the authentication cookies.
    $user = wp_signon( array(
        'user_login'    => $request->get_param( 'username' ),
        'user_password' => $request->get_param( 'password' ),
        'remember'      => true,
    ) );

    // In case of errors like wrong password, return the error object/data.
    if ( is_wp_error( $user ) ) {
        return $user;
    }

    // Now set the current user so that we get the correct nonce based on
    // that user.
    wp_set_current_user( $user->ID );

    return [
        'nonce'    => wp_create_nonce( 'wp_rest' ),
        'loggedin' => is_user_logged_in(),
        // ... your other data.
    ];
}

Additional Notes

  • REST API endpoint’s callback always receives the request object (a WP_REST_Request instance) as the first parameter, which among other functions, has get_param() for retrieving a parameter like a URL query string, a POST body data or a JSON payload, so you should make use of that and the other functions, just as I did in my example. 🙂