Ajax user authentication and registration techniques with jQuery

Learn how to use Asynchronous JavaScript + XML (Ajax) with jQuery to authenticate and sign-in a user without refreshing the browser. Then, take it a step farther by suggesting user names to a new user who's attempting to register with an existing user name.

Share:

Kris Hadlock (kris@krishadlock.com), Web Developer/Designer, Studio Sedition

Photo of Kris HadlockKris Hadlock has been a contract web developer and designer since 1996. He has worked on projects for companies such as SPIN Magazine, IKEA, United Airlines, JP Morgan Chase, GoDaddy Software, and Fire Mountain Gems. He is the author of Ajax for Web Application Developers (Sams) and The ActionScript Migration Guide (New Riders) as well as a featured columnist and writer for numerous websites and design magazines, including Peachpit.com, InformIT.com, and Practical Web Design magazine. Kris is also the founder of www.studiosedition.com, a web design and software development studio specializing in fusion of form and function.



26 October 2010

Also available in Chinese Japanese

Form submission with Ajax is a powerful technique that provides a way to send web forms without reloading the browser window. The jQuery library lets you take Ajax form submission a step farther by providing a way to quickly and easily produce Ajax-enabled web forms with minimal code. In this article, learn how to create basic Ajax form submissions with jQuery before discovering how to use this technique to authenticate a user. The article then demonstrates user registration techniques with Ajax using jQuery, such as checking for user name availability and suggesting user names when a chosen user name is already taken. Neither technique requires form submission or page reloads.

If you're not familiar with jQuery, it's essentially a JavaScript library that makes JavaScript development easier. It minimizes the code you need to write, because it has many built-in features that you would otherwise need to write custom functions or objects for. For more information and a link to download the jQuery library, see Resources; or, as you'll see in all the sample code, you can embed the current version of the jQuery library directly.

Form submission with jQuery

Submitting a form without reloading the browser can be useful in a number of scenarios. For example, you can use it to validate form fields with JavaScript code before submitting the form, to submit a form in a one-page web application, or—as this article shows—to determine whether a user name already exists during user registration. There are two ways to trigger a form submission using jQuery: using the submit handler or the click handler. Listing 1 shows how to submit a form using the submit handler.

Listing 1. Form submission with jQuery’s submit handler
<script type="text/javascript" src="http://code.jquery.com/jquery.js"></script>
<script type="text/javascript">
$(document).ready(function() {
  $('#submitForm').submit(function(e) {
    alert($('#sample').attr('value'));
    return e.preventDefault();
  });
});
</script>

<form id="submitForm" method="post">
  <input type="text" name="sample" id="sample" value="Enter something" />
  <input type="submit" id="submitBtn" value="Submit" />
</form>

Listing 2 shows how to submit a form using the click handler.

Listing 2. Form submission with jQuery’s click handler
<script type="text/javascript" 
    src="http://code.jquery.com/jquery.js"></script>
<script type="text/javascript">
$(document).ready(function() {
  $('#submitBtn').click(function(e) {
    alert($('#sample').attr('value'));
    return e.preventDefault();
  });
});
</script>

<form id="submitForm" method="post">
  <input type="text" name="sample" id="sample" value="Enter something" />
  <input type="submit" id="submitBtn" value="Submit" />
</form>

These two listings are nearly identical: They both embed the jQuery library, use the ready handler to ensure that the page is loaded before trying to access any elements, and include the same code within their handler functions. The only difference between the two is the handler and the element that the handler is assigned to. The submit handler needs to be assigned to a form element, while the click handler can be assigned to any element that can be clicked—in this case, the Submit button. To prevent the form submission from refreshing the page, you must use the preventDefault function. To access the preventDefault function, you must pass the handler event through as a parameter and use it to access the function.

Although both of the above options are valid, the submit handler is more commonly used. However, in some cases, you may have more than one Submit button, which would require a click handler on each button. Listing 3 shows a scenario in which using the click handler is necessary, because two Submit buttons can trigger the form submission.

Listing 3. Form submission with two submit buttons
<script type="text/javascript" 
    src="http://code.jquery.com/jquery.js"></script>
<script type="text/javascript" src="register.js"></script>
<div id="container">
  <div id="message"></div>
  <form method="post" id="mainform">
    <label for="username">Username</label>
    <input type="text" name="username" id="username" value="" />

    <label for="password">Password</label>
    <input type="password" name="password" value="" />

    <input type="submit" name="action" id="login" value="Log in" />

    <h2>Extra options (registration only)</h2>

    <label for="firstname">First name</label>
    <input type="text" name="firstname" value="" />

    <label for="lastname">Last name</label>
    <input type="text" name="lastname" value="" />

    <label for="email">Email</label>
    <input type="text" name="email" value="" />

    <input type="submit" name="action" id="register" value="Register" />
  </form>
</div>

In this example, notice that multiple actions can be taken with this one form: An existing user can sign in, and a new user can register by entering additional account information. Using a submit handler on the form wouldn't work in this scenario because it would not be possible to determine which button triggered the form submission. Therefore, Listing 4 uses the click handler for each button to determine what action to take so that you can then process the data accordingly.

Listing 4. Click handlers for the submit buttons in register.js
$(document).ready(function() {
  $("#register, #login").click(function(e) {
    var name = ($(event.target).attr('id') == 'register') ? 'Registration' : 'Login';
    return e.preventDefault();
  });
});

When the document is ready, you assign the click handler to the Register and Login buttons. The click handler receives a parameter, which has been named e (for event). This event object is later used to prevent default form submission, as in the earlier code listings. When the click handler is invoked, the ID of the current element being clicked is accessed and used to determine whether this is going to be a user logging in or a new registration.

Now that you know how to submit forms using jQuery, take a look at how to authenticate a user using Ajax with jQuery and PHP.


Registering and authenticating a user using Ajax with jQuery

To authenticate and register a user, you need a server-side language and a database. In this article, PHP will be the server-side language and MySQL will be the database, although you don't need to use any specific server-side language or database to create this function.

You begin by writing additional code in the JavaScript file that sends the form post using Ajax to a PHP file. The code in Listing 5 starts similarly to Listing 4, as it includes the ready handler and the click handlers for the buttons, and it determines which button was clicked. You then use the slideUp function to close the message element if it is open already. The Ajax call may not be apparent at first glance, especially if you're used to creating Ajax calls without jQuery. Because you're using a shorthand function to send the call, there isn't even any mention of Ajax in the code.

Listing 5. Submitting a web form using Ajax with jQuery
$(document).ready(function() {
  $("#register, #login").click(function(e) {
    var name = ($(event.target).attr('id') == 'register') ? 'Registration' : 'Login';
    $('#message').slideUp('fast');

    $.post('service.php', $('#mainform').serialize() 
        +'&action='+ $(event.target).attr('id'), function(data) {
      var code = $(data)[0].nodeName.toLowerCase();

      $('#message').removeClass('error');
      $('#message').removeClass('success');
      $('#message').addClass(code);
      if(code == 'success') {
        $('#message').html(name + ' was successful.');
      }
      else if(code == 'error') {
        $('#message').html('An error occurred, please try again.');
      }
      $('#message').slideDown('fast');
    });
    return e.preventDefault();
  });
});

The post function is a shorthand function equivalent to the code in Listing 6. It takes the file path to the file being requested, the serialized form data, and finally a callback function. Serializing the form data is easy with jQuery: You simply access the form element and call the serialize function to get a standard query string. The callback function first determines whether the call was a success or a failure by accessing the first node in the response: The PHP file returns this result as a node named success or error. After the status has been determined, you remove any classes that may have been left on the message element from a previous form submission, then add a class that corresponds to the success of the response. The message element is appended with HTML that states the success or error message, and you open the message using jQuery’s slideDown function.

Listing 6. jQuery Ajax function
$.ajax({
  type: 'POST',
  url: url,
  data: data,
  success: success
  dataType: dataType
});

Before you create the PHP file that will interact with the database, you need to set up the database that you plan on saving new users to and selecting existing users from. Listing 7 includes the SQL code you need to create the MySQL table named ibm_user_auth. It includes an ID, user name, password, first name, last name, and email address. The ID is set to auto-increment and is used as the primary key. The other values are all tinytext except for the password, which is set to varchar(32), because you'll use it later to save a Message-Digest algorithm 5 (MD5)-encrypted value.

Listing 7. SQL code to create a MySQL database table for users
CREATE TABLE `ibm_user_auth` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` tinytext NOT NULL,
  `password` varchar(32) NOT NULL,
  `firstname` tinytext NOT NULL,
  `lastname` tinytext NOT NULL,
  `email` tinytext NOT NULL,
  PRIMARY KEY (`id`)
);

After the table is set up, you can start writing the PHP code that will be used to interact with the database. You'll call this file—named service.php—in your Ajax post function. Listing 8 shows the code that makes up this file. It starts by defining database connection variables. After the database information is set, be sure that a user name and password were passed through the form post; if so, you extract the post data and connect to the database. Now that you're connected to the database, you need to determine whether to attempt to use the post data to log in an existing user or register him or her as a new user. You can make this determination simply by checking the action variable that was extracted from the post data and sent by the Ajax form post.

If you determine that this is a new-user registration, you also want to be sure that the first name, last name, and email address have been sent. Otherwise, you'll simply get an error. When all the requirements are met, make sure that the user name does not already exist in the database. If it does, you return an error. Otherwise, move on to validate the email address, insert the new-user data into the database, and return a success message.

If you determine that this is an existing user attempting to log in, make sure the user exists in the database. If so, save the user's data to a session, and return a success message.

Listing 8. The server-side PHP code that interacts with the JavaScript code and the database
// Database connection values
define('DB_HOST', 'localhost');
define('DB_USERNAME', 'YOUR_USERNAME');
define('DB_PASSWORD', 'YOUR_PASSWORD');
define('DB_NAME', 'YOUR_DB_NAME');

if(isset($_POST['username'], $_POST['password'])) {
  extract($_POST);

  $db = mysql_connect(DB_HOST, DB_USERNAME, DB_PASSWORD);
  mysql_select_db(DB_NAME, $db);

  if($action == 'register' 
      && isset($_POST['firstname'], $_POST['lastname'], $_POST['email'])) {
    // Verify that the username is unique
    $query = mysql_query("select count(id) 
        from ibm_user_auth where username='$username'");
    $result = mysql_fetch_row($query);
    if ( $result[0] > 0 ) {
      die("<error id='0' />");
    }

    // Validate email
    if( !preg_match("^[a-z0-9,!#\$%&'\*\+/=\?\^_`\{\|}~-]+(\.[a-z0-9,!#\$%&
      '\*\+/=\?\^_`\{\|}~-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*\.([a-z]{2,})$^", 
      $_POST['email']) ) {
      die("<error id='1' />");
    }

    mysql_query("insert into ibm_user_auth 
      (username, password, firstname, lastname, email) 
      VALUES ('$username', MD5('$password'), '$firstname', '$lastname', '$email')");
    die("<success />");
  }
  else if($action == 'login') {
    $query = mysql_query("select count(id) from ibm_user_auth where 
       username='$username' and password=md5('$password')");
    $result = mysql_fetch_row($query);
    if($result[0] == 1) {
      session_start();
      $_SESSION['username'] = $username;
      die("<success />");
    }
    else die("<error id='2' />");
  }
}

?>

Now that you've got the essentials working, it's probably a good idea to consider usability. The biggest issue with this code is that you're not telling the user what went wrong if an error occurs. However, you probably noticed that each error response includes an id attribute. The next section shows how to use these values to write an error response for each scenario in addition to suggesting user names during registration.


Handling errors and suggesting user names during registration

Handling errors with the code already covered is fairly easy at this point, especially because you're already returning errors with specific IDs that point to the problem. You already have the IDs set up, so begin by adding the PHP code that suggests user names before getting back into the JavaScript code. Listing 9 provides an example of how to create user name suggestions based on user-submitted data—in this case, the first and last names.

Listing 9. Creating user name suggestions using user-submitted data
// Database connection values
define('DB_HOST', 'localhost');
define('DB_USERNAME', 'YOUR_USERNAME');
define('DB_PASSWORD', 'YOUR_PASSWORD');
define('DB_NAME', 'YOUR_DB_NAME');

if(isset($_POST['username'], $_POST['password'])) {
  extract($_POST);

  $db = mysql_connect(DB_HOST, DB_USERNAME, DB_PASSWORD);
  mysql_select_db(DB_NAME, $db);

  if($action == 'register' 
      && isset($_POST['firstname'], $_POST['lastname'], $_POST['email'])) {
    // Verify that the username is unique
    $query = mysql_query("select count(id) 
      from ibm_user_auth where username='$username'");
    $result = mysql_fetch_row($query);
    if ( $result[0] > 0 ) {
      $out = "<error id='0'><suggestions>";
      $out .= "<suggestion>" . $firstname . $lastname . "</suggestion>";
      $out .= "<suggestion>" . $firstname . "_" . $lastname . "</suggestion>";
      $out .= "<suggestion>" . $lastname . $firstname . "</suggestion>";
      $out .= "<suggestion>" . $lastname . "_" . $firstname . "</suggestion>";
      $out .= "</suggestions></result>";
      die($out);
    }

    // Validate email
    if( !preg_match("^[a-z0-9,!#\$%&'\*\+/=\?\^_`\{\|}~-]+(\.[a-z0-9,!#\$%&
        '\*\+/=\?\^_`\{\|}~-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*\.([a-z]{2,})$^", 
      $_POST['email']) ) {
      die("<error id='1' />");
    }

    mysql_query("insert into ibm_user_auth 
      (username, password, firstname, lastname, email) 
      VALUES ('$username', MD5('$password'), '$firstname', '$lastname', '$email')");
    die("<success />");
  }
  else if($action == 'login') {
    $query = mysql_query("select count(id) from ibm_user_auth 
        where username='$username' 
        and password=md5('$password')");
    $result = mysql_fetch_row($query);
    if($result[0] == 1) {
      session_start();
      $_SESSION['username'] = $username;
      die("<success />");
    }
    else die("<error id='2' />");
  }
}

?>

Notice that now when a user name already exists during registration, you create an XML structure that includes various combinations of user-submitted data that make up suggested user names. You could even take this a step farther by ensuring that suggestions don't exist in the database before returning them.

Listing 10 shows how you handle displaying the suggested names using jQuery.

Listing 10. Displaying suggested user names using jQuery
$(document).ready(function() {
  $("#register, #login").click(function(e) {
  var name = ($(event.target).attr('id') == 'register') ? 'Registration' : 'Login';
  $('#message').slideUp('fast');

  $.post('service.php', 
      $('#mainform').serialize() +'&action='+ $(event.target).attr('id'), 
          function(data) {
    var code = $(data)[0].nodeName.toLowerCase();

    $('#message').removeClass('error');
    $('#message').removeClass('success');
    $('#message').addClass(code);
    if(code == 'success') {
      $('#message').html(name + ' was successful.');
    }
    else if(code == 'error') {
      var id = parseInt($(data).attr('id'));
      switch(id) {
        case 0:
          $('#message').html('This user name has already been taken. 
              Try some of these suggestions:

'); form = $(document.createElement('form')); $(data).find('suggestions > suggestion').each(function(idx, el) { radio = $(document.createElement('input')); radio.attr({type: 'radio', name: 'suggested', id: 'suggested_'+idx, value: el.innerHTML}); lbl = $(document.createElement('label')); lbl.attr('for', 'suggested_'+idx); lbl.html(el.innerHTML); form.append(radio); form.append(lbl); form.append('
'); }); $('#message').append(form); $('#message form input[type="radio"]').click(function() { $('#username').val($(this).attr('value')); }); break; case 1: $('#message').html('The e-mail entered is invalid.'); break; case 2: $('#message').html('The user name or password you entered was invalid.'); break; default: $('#message').html('An error occurred, please try again.'); } } $('#message').slideDown('fast'); }); return e.preventDefault(); }); });

Now, when an error is returned, you check for the error ID rather than simply displaying a default error message that isn't helpful to the user. First, you parse the ID from the XML structure returned from PHP, then you use a switch statement to direct the error to the appropriate message or associated code. The first error ID is for when a user name already exists in the system. This is where you access the suggested user names and display them for the user to choose a new name. Start by accessing the suggestions nodes and iterating through each one. During iteration, you create a radio button and a label that includes the suggestion, then you append it to a custom error message and display it to the user. At this point, the user can choose a suggested name, which will automatically be added to the user name text input, and then proceed with registration.

The next error ID is for invalid email addresses. The associated code simply displays a custom error message telling the user what went wrong. You could even add a line of code to highlight the incorrect field. Next is a custom error message for failed logins. The code uses a message that is more ambiguous in this case, because for security reasons you don't want to tell just anyone which field was incorrect. Finally, the default message is the same message you displayed in Listing 5. This message will most likely never be used, but it's good to have as a fall-back.


Conclusion

User authentication with Ajax is becoming increasingly more common and is almost a requirement for single-page web applications. It's also extremely beneficial for suggesting user names, as you did in this article, because it gives a user false hope when a page is submitted, only to refresh with error messages. This way, responses are more automatic and user friendly, providing a more usable web experience.


Download

DescriptionNameSize
Sample Ajax scripts used in this articleAjaxUserAuthentication.zip5KB

Resources

Learn

Get products and technologies

Discuss

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into Web development on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Web development
ArticleID=555923
ArticleTitle=Ajax user authentication and registration techniques with jQuery
publish-date=10262010