0

Summary

Ajax request to a back-end plugin PHP script successfully logs in the user and sets login cookies in the browser. However, the page making the call fails to recognize those cookies, and is_user_logged_in() returns false.

Update

Additional testing reveals that the error is browser-specific. The login as described below works just fine in Firefox; but it fails in Safari and Chrome. (Haven't recently tested in IE.) I now feel certain this is a cookie permissions issue related to my screenshot, below. It appears that WordPress is setting the login cookie with the path: siteurl.tld/wp-content/plugins/ instead of with the domain root as the cookie path. As shown in the screenshot below, WordPress sets 3 login-related cookies with different paths. This feature was fully functional until a few months ago. I might speculate that some browsers have updated their cookie security policies at about that time, and began to block the login cookie and cause is_user_logged_in() to return false. The question remains: how to work around this issue? However, as time goes on, the question is becoming academic, because I'm going to go ahead and rewrite this entire portion of the application so it won't involve the Ajax login at all.

Details

My application offers an optional login for existing users in the first step of a multi-step process. If the user enters their login info, a JavaScript sends an Ajax request to a back-end PHP script:

[edit, I have added the full Ajax call here, as well as the code that processes it, per requests from other users. However, please note as stated in the comments below, the call to wp_signon() is successful, and returns a valid WP_User object, and sets cookies in the browser. Most of this code is run before that point. The real problem is after that: the cookies set by this Ajax request are not being recognized by the page that made the call. ]

                        var data = {
                            action: "my_ajax_login",
                            email: $email.val(),
                            pass: $pass.val(),
                            login_nonce: $form.find( '[name="my_login_nonce"]' ).val()
                        };

                        $.post( ajaxurl, data ).done( function( resp ) {
                            
                            if ( resp.success ) {
                                // refresh the page
                                location.reload();
                            } else {
                                // continue as a guest user
                            }
                       }

The PHP script uses the passed data to log the user in using wp_signon():

[ edit: expanded to include additional source code per request ]

        if( !wp_verify_nonce( $_POST['my_login_nonce'], 'my_login_nonce' ) ){
            do_action( 'my_debugging_tool_log_error', "failed the login nonce");
            wp_die();
        }
    
        $error_message = __( 'Oops! You entered an invalid username/email and or password.', 'textdomain' );

        $user_login = my_get_post_value( 'email ', '' );

        if ( is_email( $user_login ) ) {
            $user = get_user_by( 'email', $user_login );

            if ( !$user instanceof WP_User ) {
                wp_send_json_error( array(
                    'message' => $error_message,
                ) );
            }

            $user_login = $user->user_login;
        }

        $info = array(
            'user_login'     => $user_login,
            'user_password'  => my_get_post_value( 'pass', '' ),
            'remember'       => true,
        );

        // this is the key line:
        $user_signon = wp_signon( $info );

        if ( is_wp_error( $user_signon ) ) {
            wp_send_json_error( array(
                'message' => $error_message,
            ) );
        }

        wp_send_json_success();

That all seems to work fine, and WordPress sets the login cookies, I can see them in my browser tools, as illustrated in this screenshot:

admin-ajax requests and cookies

Caveat: as you can see, one of those cookies is set within the "plugins" directory, which my browser (Chrome) is blocking for security reasons with the message, "This cookie was blocked because its path was not an exact match for or a superdirectory of the request url's path." However, that seems to be a red herring: I don't believe it's the cause of the failed signon. Please correct me if I'm wrong!

To review: as shown above, the signon is processed by the admin-ajax.php script, which sets the login cookies in the browser and returns a wp_send_json_success() to the Ajax caller, which then refreshes the page so the nonces will all be in accordance with the recently logged-in user.

But when the page refreshes, it checks to see if the user is logged in:

if ( !is_user_logged_in() ){
     // show the login form...
}

And it always shows the login form! Additional testing with var_dump($current_user) and var_dump(wp_get_current_user()) show that, as far as the calling page is concerned, the current user ID is 0. The user is not logged in, even though the wordpress_logged_in cookie is clearly set in the browser.

Any insight would be greatly appreciated.

6
  • Show the javascript and php code for the ajax request. – shanebp Jan 10 at 19:01
  • @shanebp You got it! Question has been edited with additional code added. Thank you! – nightowl Jan 10 at 19:36
  • window.location.href = window.location.href -- OR -- window.location.reload() -- the first fails if there is a hash in the querystring and the second loads from cache, by default - but can be overridden. – Q Studio Jan 10 at 21:35
  • @QStudio Tested just now with location.reload() -- looking at online docs I agree that's probably a better-supported syntax, but it did not resolve this issue. I suspect the deeper problem has something to do with cookie paths or permissions, possibly related to the way wp_signon() works? but I just don't know why the problem occurs or how to resolve it. Thanks for your feedback! – nightowl Jan 10 at 21:59
  • There are lots of places this code could have problems - you do not show my_get_post_value() function, you pass a nonce, but do not validate it, and you also could debug the variable $info before passing it to wp_signon to see what it includes and the return value from wp_signon to see what WP thinks. – Q Studio Jan 11 at 7:46

Your Answer

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

Browse other questions tagged or ask your own question.