Adding a front end login form that uses AJAX to log users in is a relatively simple procedure, but requires a little understanding about how WordPress handles authentication. WordPress offers a great function to enable us to log users in programmatically called wp_signon. we simply need to pass the user credentials to wp_signon and let WordPress handle the rest. If there is an error logging in the user such as a bad username or password, then WordPress will return a WP_Error and we can go from there.

To begin, lets create an html form. The form names are not important, but we’ll be referencing them when we make our ajax call.

<!-- Login Form -->

<h3>Sign In</h3>

<form id="loginForm" action="login" method="post">

    <p class="login-message"></p>

    <div>
        <label for='username'>Username</label>
        <input id="username" type="text" name="username" />
    </div>
    <div>
        <label for='password'>Password</label>
        <input id="password" type="password" name="password" />

    </div>

    <div>
        <input name="rememberme" type="checkbox" id="rememberme" value="forever">
        <label for="rememberme">Remember Me</label>
    </div>

    <input type="submit" name="submit" value="Login">

    <?php wp_nonce_field( 'ajax-login-nonce', 'security' ); ?>
</form>

The next step is to override form submission and use AJAX to submit the form. We’ll need a javascript event handler and a function to make an ajax call, I’m using jQuery for this. The form names are again not important, but we’ll be referencing them server side to pass to wp_signon.

// script file

$('#loginForm').on('submit', login.login);
login: {
    login: function(e) {
        e.preventDefault();

        var $form = $(this);
        var data = {
            'action'    : 'login_check',
            'username'  : $form.find('#username').val(),
            'password'  : $form.find('#password').val(),
            'rememberme': $form.find('#rememberme').is( ':checked' ) ? true : false,
            'security'  : $form.find('#security').val()
        };

        $.ajax({
            url: ajaxUrl, // your ajax url
            type: 'POST',
            dataType: 'json',
            data: data,
            beforeSend: function(jqXHR, settings) { $('.login-message').html(''); },
            success: function(data, textStatus, xhr) { login.onAjaxSuccess(data); },
            error: function(jqXHR, textStatus, errorThrown) {  $('.login-message').html('There was an unexpected error'); }
        });
    },
    onAjaxSuccess: function(data) {

        if (typeof data.message !== 'undefined')
            $('.login-message').html(data.message);

        // reload on success
        if (typeof data.success !== 'undefined' && data.success === true) {
            location.reload();
        }
    }
};

Now we need to handle the AJAX request server side. Simply add an action and a function to call. In the function that is triggered by the AJAX request we’ll need to get our form values and save them to an array. The array keys for the array of user credentials are important as wp_signon will need to know what array keys to reference. The keys are “user_login”, “user_password”, and “remember”. We need to process the security nonce that we included in our form. We also need to handle any errors that wp_signon returns, then we can just echo success/failure to the client.

// functions.php

add_action( 'wp_ajax_nopriv_login_check', 'loginCheck' );
add_action( 'wp_ajax_login_check', 'loginCheck' );

/**
* login check
*/
function loginCheck() {

    if ( is_user_logged_in() ) {

        echo json_encode( array( 'success' => true, 'message' => 'You are already logged in' ) );
        die;
    }

    // check the nonce, if it fails the function will break
    check_ajax_referer( 'ajax-login-nonce', 'security' );

    // get the POSTed credentials
    $creds = array();
    $creds['user_login']    = !empty( $_POST['username'] ) ? $_POST['username'] : null;
    $creds['user_password'] = !empty( $_POST['password'] ) ? $_POST['password'] : null;
    $creds['remember']      = !empty( $_POST['rememberme'] ) ? $_POST['rememberme'] : null;

    // check for empty fields
    if( empty( $creds['user_login'] ) || empty( $creds['user_password'] ) ) {

        echo json_encode( array( 'success' => true, 'message' => 'The username or password is cannot be empty' ) );
        die;
    }

    // check login
    $user = wp_signon( $creds, false );

    if ( is_wp_error( $user ) ) {

        if ( $user->get_error_code() == "invalid_username" || $user->get_error_code() == "incorrect_password" ) {

            echo json_encode( array( 'success' => true, 'message' => 'The username or password is incorrect' ) );
            die;

        } else {

            echo json_encode( array( 'success' => true, 'message' => 'There was an error logging you in' ) );
            die;
        }

        echo json_encode( array( 'success' => true, 'message' => 'Login successful' ) );
        die;
    }

    echo json_encode( $return );
    die;
}

This should be all you need to have a fully functioning front-end login form for your site. I should note that you’ll want to do something similar for your reset password form so users don’t need to visit wp-login.php for that either.

6 comments on “Adding an AJAX Login Form to a WordPress Theme

  1. Robin Wilson says:

    Great article, and thanks for writing this.

    What I don’t get is am I creating separate files, or is this all in one file, what are these files called, and what type and where do I put the files I created?

    Ok so the functions part can sit in my functions file. But is the ‘form’ a file, and is so what type? and is the script file another file, and again if so what is it called and where in wordpress would I put these.

    Thanks !

  2. The Guy says:

    There is a syntax error in the JS file.

    “Uncaught SyntaxError: Unexpected token (” at the line of “login: function(e){“

  3. Joe Turner says:

    Really nice implementation for this problem. There is one problem though. No matter the response (login successful, login failure or error) you always return ‘success’ => true in the JSON. this means the pages always refreshes, even if there is an error.
    Swap this for ‘success’ => true for all responses other than login successful in functions.php and test for this in the ajax success closure and you have a working solution.

    1. Joe Turner says:

      I meant swap for ‘success’ => false

  4. Firas Kudsy says:

    Thanks for your this artical
    i tried the above code, but am getting “There was an unexpected error”.
    i did some changes till the code work withing Visual studio 2013 with Apache cordova tools installed, here is my Javascript code:

    $(document).ready(function() {
    $(‘#loginForm’).on(‘submit’, login);

    function login(e) {
    e.preventDefault();

    var $form = $(this);
    var data = {
    ‘action’: ‘login_check’,
    ‘username’: $form.find(‘#username’).val(),
    ‘password’: $form.find(‘#password’).val(),
    //’rememberme’: $form.find(‘#rememberme’).is(‘:checked’) ? true : false,
    // ‘security’: $form.find(‘#security’).val()
    };

    $.ajax({
    url: ‘localhost:8080/wordpress/wp-admin/admin-ajax.php’,
    type: ‘POST’,
    dataType: ‘json’,
    data: data,
    // beforeSend: function (jqXHR, settings) { $(‘.login-message’).html(”); },
    success: function (data, textStatus, xhr) { onAjaxSuccess(data); },
    error: function (jqXHR, textStatus, errorThrown) { $(‘.login-message’).html(‘There was an unexpected error’); }
    });
    }
    function onAjaxSuccess(data) {

    if (typeof data.message !== ‘undefined’)
    $(‘.login-message’).html(data.message);

    // reload on success
    if (typeof data.success !== ‘undefined’ && data.success === true) {
    location.reload();
    }
    }
    });

    but when am sending the same form data using the postman chrome extension, i get success !!?? cant see where is the problem ?

  5. Dmitriy says:

    This doesn’t seem to sanitize the input on the server side which is a high security risk. You can fix it by changing array items code like this:

    $creds[‘user_login’] = !empty( $_POST[‘username’] ) ? sanitize_text_field( $_POST[‘username’] ) : null;
    $creds[‘user_password’] = !empty( $_POST[‘password’] ) ? sanitize_text_field( $_POST[‘password’] ) : null;
    $creds[‘remember’] = !empty( $_POST[‘rememberme’] ) ? sanitize_text_field( $_POST[‘rememberme’] ) : null;

Comments are closed.