PHP Login System with Source Code

In this tutorial, we are going to build a PHP login form. This form consists of a simple login page in PHP with MySQL database. If you are looking for a script to build PHP login and registration pages. This tutorial is for you. You can download all the code by following this tutorial.

This script does not rely on any framework, everything is built with vanilla PHP code. The knowledge required is basic PHP and MySQL. And we are going to use Tailwind CSS to make our form look pretty.

At the end of this tutorial, you will build a good looking and fully functional PHP login form as shown below:

Database setup

User accounts are stored in the MySQL database. And for security purposes, the password field is hashed before storing in the database.

Firstly, let's create a database table to store the users' accounts.

CREATE TABLE users (
	id INT(6) UNSIGNED AUTO_INCREMENT PRIMARY KEY,
	username VARCHAR(30) NOT NULL,
	password TEXT NOT NULL,
	CONSTRAINT constraint_name UNIQUE (username)
)

We have added a constraint to the table for a unique username, so there won't be duplicated usernames.

User account manipulation

We need a User class that is capable of creating a user account and verifying a user's password.

Let's create a file User.php with the code below:

<?php

class User
{
    private $dbh;

    private $usersTableName = 'users';

    public function __construct($database, $host, $databaseUsername, $databaseUserPassword)
    {
        try {

            $this->dbh =
                new PDO(sprintf('mysql:host=%s;dbname=%s', $host, $database),
                    $databaseUsername,
                    $databaseUserPassword
                );

        } catch (PDOException $e) {
            die($e->getMessage());
        }
    }

    public function create($username, $password)
    {
        $password = $this->passwordHashed($password);

        $statement = $this->dbh->prepare(
            'INSERT INTO '.$this->usersTableName.' (username, password) VALUES (:username, :password)'
        );

        if (false === $statement) {
            throw new Exception('Invalid prepare statement');
        }

        if (false === $statement->execute([
                ':username' => $username,
                ':password' => $password,
            ])) {
            throw new Exception(implode(' ', $statement->errorInfo()));
        }
    }

    public function exists($username, $password)
    {
        $statement = $this->dbh->prepare(
            'SELECT * from '.$this->usersTableName.' where username = :username'
        );

        if (false === $statement) {
            throw new Exception('Invalid prepare statement');
        }

        $result = $statement->execute([':username' => $username]);

        if (false === $result) {
            throw new Exception(implode(' ', $statement->errorInfo()));
        }

        $row = $statement->fetch(PDO::FETCH_ASSOC);

        if (!is_array($row)) {
            return false;
        }

        return password_verify($password, $row['password']);
    }

}

There are two public API in the code above:

  • The method create: this method is used to create an account. It uses password_hash() to hash the password. The function password_hash is used together with password_verify which you will see next.
  • The method exists: this method is used to check if a user record exists in the database. The logic is to find the record using the username field, if it is found, we use password_verify to verify if the password is correct. This is the latest and the safest way in PHP currently to do password checking.

In PHP, we used to use MD5 and SHA to hash a password and compare the stored hash directly when we need to do password verification. However, this technique is not safe anymore. MD5 and SHA algorithms are too weak for today’s computational power, if an attacker steals the hash, they can steal the password.

Insert user account

We are going to create a script that will make the account creation task easy for us.

This is pretty easy to do with the help of User class. Create a file insert-user.php:

<?php

include 'User.php';

$user = new User('php_login_system', '127.0.0.1', 'root','root');

$user->create('admin','password');

Update the database access info accordingly and run the command from the console:

php insert-user.php

You should have created a user account with the username admin and the password password. You can modify this script to insert any accounts with desired username and password.

Authentication

The authentication flow happens when a user submits the login form. The script will first verify the username and password, if they are correct, it will assign a session to the user, then the user can use this session to navigate to the member page.

Let's complete the session creation part.

Create a file login.php with the content below:

<?php

include 'User.php';

$user = new User('php_login_system', '127.0.0.1', 'root', 'root');

$username = $_POST['username'];
$password = $_POST['password'];

if ($user->exists($username, $password)) {
    $_SESSION['login'] = true;
    header("member.php'));
} else {
    header("index.php?message=".urlencode('incorrect username or password'));
    exit();
}

This page gets the username and password via the $_POST variable and verifies its existence and assigns true to a session variable $_SESSION['login']. When this variable is true, it means the user is logged in, and the page will be redirected to member.php, a fake member page.

If the credential is incorrect, it will redirect to the index.php, which is the PHP login form page.

We have left some pages to complete. Let's move on.

Login form page

This is the actual login form page. It is pretty straightforward, we build an HTML form that posts to login.php page.

Create a file index.php with the content below:

<!doctype html>
<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script src="https://cdn.tailwindcss.com"></script>
</head>
<body>

<form action="login.php" method="post">
    <div class="relative flex min-h-screen flex-col justify-center overflow-hidden bg-gray-50 py-6 sm:py-12">
        <div class="relative bg-white pt-10 pb-8 px-10 shadow-xl mx-auto w-96 rounded-lg">
            <div class="divide-y divide-gray-300/50 w-full">
                <div class="space-y-6 py-8 text-base  text-gray-600">

                    <?php if (isset($_GET['message'])): ?>
                        <p class="text-sm text-red-500"><?= $_GET['message']; ?></p>
                    <?php endif; ?>

                    <p class="text-xl font-medium leading-7">PHP Login System</p>
                    <div class="space-y-4 flex flex-col">
                        <input type="text"
                               name="username"
                               placeholder="Username"
                               class="border border-gray-300/50 p-1 rounded focus:outline-none"/>

                        <input type="password"
                               name="password"
                               placeholder="Password"
                               class="border border-gray-300/50 p-1 rounded focus:outline-none"/>
                    </div>
                </div>
                <div class="pt-8 text-base font-semibold leading-7">
                    <button type="submit" class="bg-sky-500 hover:bg-sky-600 px-4 py-1 text-white rounded">
                        Login
                    </button>
                </div>
            </div>
        </div>
    </div>
</form>


</body>
</html>

As mentioned above, this is a very simple HTML page. The only part we like to highlight is the message, we will display a warning message if the $_GET['message'] is set. This is useful for alerting users the application's status.

Member page

We will also create a fake member page, there is nothing special inside. If you are creating your member pages, you can mimic the top logic to create your own.

Create a file member.php:

<?php
session_start();
if (!isset($_SESSION['login']) || !$_SESSION['login'] ) {
    header("Location: index.php?message=".urlencode('please login first'));
    exit();
}
?>

<!doctype html>
<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script src="https://cdn.tailwindcss.com"></script>
</head>
<body>

<div class="relative flex min-h-screen flex-col justify-center overflow-hidden bg-gray-50 py-6 sm:py-12">
    <div class="relative bg-white pt-10 pb-8 px-10 shadow-xl mx-auto rounded-lg">
        <div class="divide-y divide-gray-300/50 w-full">
            <div class="space-y-6 py-8 text-base text-gray-600">
                <p class="text-xl font-medium leading-7">Welcome to the PHP Login System member page</p>
            </div>

            <div class="pt-8 text-base font-semibold">
                <a href="logout.php" class="bg-sky-500 hover:bg-sky-600 px-4 py-2 text-white rounded">Logout</a>
            </div>
        </div>
    </div>
</div>


</body>
</html>

Pay attention to the top of the page. The PHP code has to stay on top for the session check to work. The logic is simple, it checks if the $_SESSION['login'] is true, otherwise, it will redirect users to the login page.

Logout page

There is a logout button in the member.php, which redirects to logout.php. On this page, we set the $_SESSION['login'] to be false and redirect the page to the login form page:

<?php
session_start();
$_SESSION['login'] = false;
header("Location: index.php");

The end

If you have followed the tutorial correctly, you should have built a fully functional and secure login page as shown below: