#native_company# #native_desc#
#native_cta#

Implementing User Authentication and Session Management with PHP

By W. Jason Gilmore
on May 17, 2011

Outside of installation and configuration issues, reader questions pertaining to user authentication and session management almost certainly rank among the most common I receive on an ongoing basis. The logic itself is pretty straightforward; however, even a simple implementation involves a number of small but important details which aren’t always so easy to figure out the first time around. This tutorial serves to dispel much of the confusion by guiding you through the implementation of a simple user authentication feature which will subsequently keep the user logged in via a session.

The Login Form

Let’s start with the easiest part of the feature, creating the login form. There’s not much to describe here, as this form consists of just username and password fields, presented here:
<form action="login.php" enctype="application/x-www-form-urlencoded" method="post">

<div class="articlePara">
<label for="username">Your username:</label><br />
<input name="username" size="25" type="text" />
</div>

<div class="articlePara">
<label for="password">Your password:</label><br />
<input name="password" size="25" type="password" />
</div>

<div class="articlePara">
<input name="submit" type="submit" value="Login" />
</div>
</form>
When rendered to the browser the form looks like that found in Figure 1.


Figure 1. The Login Form

The MySQL Accounts Table

Next, we’ll create the MySQL table used to manage the user accounts. Of course, one would presume this already exists since some sort of registration mechanism should be in place. In its simplest form this table should consist of an integer-based primary key, unique username field, and a hashed password field. Optionally, the table would also include a field denoting the last time the user logged into the website, which is useful for learning more about usage activity:
CREATE TABLE accounts (
  id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
  username VARCHAR(255) NOT NULL UNIQUE,
  password CHAR(32) NOT NULL,
  last_login DATETIME NOT NULL
);

Authenticating the User

You probably noticed that the login form points to a script named login.php. In a real-world situation the form and the login script would probably be one in the same, but for the purposes of this exercise, I’ll treat them separately so as not to muddy the water in terms of explaining the authentication process. A heavily commented login.php script follows; take some time to review this code, after which I’ll offer a summary of key lines:
<?php

  // Sanitize incoming username and password
  $username = filter_var($_POST['username'], FILTER_SANITIZE_STRING);
  $password = filter_var($_POST['password'], FILTER_SANITIZE_STRING);

  // Connect to the MySQL server
  $db = new mysqli("localhost", "root", "jason", "phpbuilder");

  // Determine whether an account exists matching this username and password
  $stmt = $db->prepare("SELECT id FROM accounts WHERE username = ? and password = md5(?)");

  // Bind the input parameters to the prepared statement
  $stmt->bind_param('ss', $username, $password); 

  // Execute the query
  $stmt->execute();

  // Store the result so we can determine how many rows have been returned
  $stmt->store_result();

  if ($stmt->num_rows == 1) {

    // Bind the returned user ID to the $id variable
    $stmt->bind_result($id); 
    $stmt->fetch();

    // Update the account's last_login column
    $stmt = $db->prepare("UPDATE accounts SET last_login = NOW() WHERE id = ?");
    $stmt->bind_param('d', $id); 
    $stmt->execute();

    // Redirect the user to the home page
    header('Location: http://www.example.com');
  }

?>
The login.php script begins by sanitizing incoming form input using PHP’s filter extension. Although the subsequent prepared statement will help to prevent any potentially malicious input from damaging or circumventing the authentication system, an additional layer of security never hurts.
Following that, we connect to the MySQL server and determine whether a row matching the provided username and password exists. Notice how MySQL’s md5() function is used to first hash the incoming password. This is because the password was presumably hashed using the same md5() function at registration time. Storing passwords in plain text is never a good idea, and the md5() function is an easy way to store them in a format which can’t be further exploited should an attacker somehow obtain your database.
If a matching row is located, the associated ID is obtained and subsequently used to update the row’s last_login column. Notice how another MySQL function is used (NOW()) to accomplish this task.
Finally, the user is redirected to the website home page.