PHP – Use Case 2: Implementing a User Authentication System (Registration and Login)

Project Overview

A User Authentication System allows users to register, log in, and access protected areas of a website. This project covers user registration, login, session management, password hashing, and access control using PHP and MySQL.

Prerequisites

Ensure you have the following:

  • Web Server: Apache (using XAMPP, WAMP, or MAMP)
  • PHP: Version 7.4 or higher
  • MySQL: For database management
  • Code Editor: VS Code, Sublime Text, PHPStorm, etc.
  • Composer: For dependency management (optional)

Step-by-Step Procedure

1. Setting Up the Development Environment

  1. Install XAMPP:
    • Download from XAMPP Official Website.
    • Follow the installation wizard and install it in the default directory.
  2. Start Apache and MySQL:
    • Open the XAMPP Control Panel.
    • Start the Apache and MySQL modules.

2. Creating the Database

  1. Access phpMyAdmin:
    • Navigate to http://localhost/phpmyadmin/ in your browser.
  2. Create a New Database:
    • Click on “New” in the left sidebar.
    • Name the database user_auth.
    • Choose “utf8mb4_unicode_ci” as the collation.
    • Click “Create”.
  3. Create a users Table:
    • Select the user_auth database.
    • Click on “New” to create a table.
    • Define the table with the following fields:
    Field Type Null Key Default Extra id INT NO PRI NULL AUTO_INCREMENT username VARCHAR(50) NO UNI NULL email VARCHAR(100) NO UNI NULL password VARCHAR(255) NO NULL created_at TIMESTAMP NO CURRENT_TIMESTAMP
    • Click “Save”.

3. Project Structure

Organize your project files as follows:

user-auth/
├── assets/
│   ├── css/
│   │   └── styles.css
│   └── js/
│       └── scripts.js
├── config/
│   └── db.php
├── templates/
│   ├── header.php
│   └── footer.php
├── register.php
├── login.php
├── logout.php
├── dashboard.php
└── README.md

4. Configuration

a. Database Connection (config/db.php)
<?php
// config/db.php

$host = 'localhost';
$db   = 'user_auth';
$user = 'root'; // Default XAMPP MySQL user
$pass = '';     // Default XAMPP MySQL password is empty
$charset = 'utf8mb4';

$dsn = "mysql:host=$host;dbname=$db;charset=$charset";

$options = [
    PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION, // Enable exceptions
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,       // Fetch associative arrays
    PDO::ATTR_EMULATE_PREPARES   => false,                  // Disable emulation
];

try {
    $pdo = new PDO($dsn, $user, $pass, $options);
} catch (\PDOException $e) {
    // Handle connection errors
    throw new \PDOException($e->getMessage(), (int)$e->getCode());
}
?>

5. Creating Reusable Templates

a. Header (templates/header.php)
<?php
// templates/header.php
session_start();
?>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>User Authentication System</title>
    <link rel="stylesheet" href="/user-auth/assets/css/styles.css">
</head>
<body>
    <header>
        <h1>User Authentication System</h1>
        <nav>
            <?php if (isset($_SESSION['user_id'])): ?>
                <a href="/user-auth/dashboard.php">Dashboard</a>
                <a href="/user-auth/logout.php">Logout</a>
            <?php else: ?>
                <a href="/user-auth/login.php">Login</a>
                <a href="/user-auth/register.php">Register</a>
            <?php endif; ?>
        </nav>
    </header>
    <main>
b. Footer (templates/footer.php)
<?php
// templates/footer.php
?>
    </main>
    <footer>
        <p>&copy; <?php echo date("Y"); ?> Your Company Name</p>
    </footer>
    <script src="/user-auth/assets/js/scripts.js"></script>
</body>
</html>

6. Styling the Application (assets/css/styles.css)

Add basic styles to enhance the appearance.

/* assets/css/styles.css */

body {
    font-family: Arial, sans-serif;
    margin: 0;
    padding: 0;
}

header, footer {
    background-color: #444;
    color: #fff;
    padding: 10px 20px;
}

header h1, footer p {
    margin: 0;
}

nav a {
    color: #fff;
    margin-right: 15px;
    text-decoration: none;
}

main {
    padding: 20px;
}

form {
    max-width: 400px;
    margin: auto;
}

.form-group {
    margin-bottom: 15px;
}

label {
    display: block;
    margin-bottom: 5px;
}

input[type="text"],
input[type="email"],
input[type="password"] {
    width: 100%;
    padding: 8px;
    box-sizing: border-box;
}

button {
    padding: 10px 15px;
    background-color: #007BFF;
    color: #fff;
    border: none;
    cursor: pointer;
}

button:hover {
    background-color: #0069d9;
}

.error {
    background-color: #f2dede;
    color: #a94442;
    padding: 15px;
    margin-bottom: 20px;
    border: 1px solid #ebccd1;
    border-radius: 4px;
}

.success {
    background-color: #dff0d8;
    color: #3c763d;
    padding: 15px;
    margin-bottom: 20px;
    border: 1px solid #d6e9c6;
    border-radius: 4px;
}

7. User Registration (register.php)

This page allows new users to create an account.

<?php
// register.php

require 'config/db.php';
require 'templates/header.php';

$errors = [];
$success = '';

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    // Sanitize and validate inputs
    $username = trim($_POST['username']);
    $email    = trim($_POST['email']);
    $password = trim($_POST['password']);
    $confirm_password = trim($_POST['confirm_password']);

    // Validation
    if (empty($username)) {
        $errors[] = 'Username is required.';
    }

    if (empty($email)) {
        $errors[] = 'Email is required.';
    } elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
        $errors[] = 'Invalid email format.';
    }

    if (empty($password)) {
        $errors[] = 'Password is required.';
    } elseif (strlen($password) < 6) {
        $errors[] = 'Password must be at least 6 characters.';
    }

    if ($password !== $confirm_password) {
        $errors[] = 'Passwords do not match.';
    }

    // Check if username or email already exists
    if (empty($errors)) {
        $stmt = $pdo->prepare('SELECT id FROM users WHERE username = ? OR email = ?');
        $stmt->execute([$username, $email]);
        if ($stmt->fetch()) {
            $errors[] = 'Username or email already exists.';
        }
    }

    if (empty($errors)) {
        // Hash the password
        $hashed_password = password_hash($password, PASSWORD_DEFAULT);

        // Insert into database
        $stmt = $pdo->prepare('INSERT INTO users (username, email, password) VALUES (?, ?, ?)');
        if ($stmt->execute([$username, $email, $hashed_password])) {
            $success = 'Registration successful. You can now <a href="login.php">login</a>.';
            // Clear form fields
            $username = $email = $password = $confirm_password = '';
        } else {
            $errors[] = 'There was an error registering. Please try again.';
        }
    }
}
?>

<h2>Register</h2>

<?php if (!empty($errors)): ?>
    <div class="error">
        <ul>
            <?php foreach($errors as $error): ?>
                <li><?php echo htmlspecialchars($error); ?></li>
            <?php endforeach; ?>
        </ul>
    </div>
<?php endif; ?>

<?php if ($success): ?>
    <div class="success">
        <p><?php echo $success; ?></p>
    </div>
<?php endif; ?>

<form action="register.php" method="POST">
    <div class="form-group">
        <label for="username">Username:</label>
        <input type="text" id="username" name="username" value="<?php echo htmlspecialchars($username ?? ''); ?>" required>
    </div>

    <div class="form-group">
        <label for="email">Email:</label>
        <input type="email" id="email" name="email" value="<?php echo htmlspecialchars($email ?? ''); ?>" required>
    </div>

    <div class="form-group">
        <label for="password">Password (min 6 characters):</label>
        <input type="password" id="password" name="password" required>
    </div>

    <div class="form-group">
        <label for="confirm_password">Confirm Password:</label>
        <input type="password" id="confirm_password" name="confirm_password" required>
    </div>

    <button type="submit">Register</button>
</form>

<?php
require 'templates/footer.php';
?>

8. User Login (login.php)

This page allows existing users to log in.

<?php
// login.php

require 'config/db.php';
require 'templates/header.php';

$errors = [];
$success = '';

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    // Sanitize and validate inputs
    $username = trim($_POST['username']);
    $password = trim($_POST['password']);

    if (empty($username)) {
        $errors[] = 'Username is required.';
    }

    if (empty($password)) {
        $errors[] = 'Password is required.';
    }

    if (empty($errors)) {
        // Fetch user from database
        $stmt = $pdo->prepare('SELECT id, password FROM users WHERE username = ?');
        $stmt->execute([$username]);
        $user = $stmt->fetch();

        if ($user && password_verify($password, $user['password'])) {
            // Password is correct, start a session
            $_SESSION['user_id'] = $user['id'];
            $_SESSION['username'] = $username;
            // Redirect to dashboard
            header('Location: dashboard.php');
            exit;
        } else {
            $errors[] = 'Invalid username or password.';
        }
    }
}
?>

<h2>Login</h2>

<?php if (!empty($errors)): ?>
    <div class="error">
        <ul>
            <?php foreach($errors as $error): ?>
                <li><?php echo htmlspecialchars($error); ?></li>
            <?php endforeach; ?>
        </ul>
    </div>
<?php endif; ?>

<?php if ($success): ?>
    <div class="success">
        <p><?php echo htmlspecialchars($success); ?></p>
    </div>
<?php endif; ?>

<form action="login.php" method="POST">
    <div class="form-group">
        <label for="username">Username:</label>
        <input type="text" id="username" name="username" value="<?php echo htmlspecialchars($username ?? ''); ?>" required>
    </div>

    <div class="form-group">
        <label for="password">Password:</label>
        <input type="password" id="password" name="password" required>
    </div>

    <button type="submit">Login</button>
</form>

<?php
require 'templates/footer.php';
?>

9. User Dashboard (dashboard.php)

This is a protected page accessible only to logged-in users.

<?php
// dashboard.php

require 'config/db.php';
require 'templates/header.php';

// Check if user is logged in
if (!isset($_SESSION['user_id'])) {
    header('Location: login.php');
    exit;
}

// Fetch user information
$stmt = $pdo->prepare('SELECT username, email, created_at FROM users WHERE id = ?');
$stmt->execute([$_SESSION['user_id']]);
$user = $stmt->fetch();
?>

<h2>Welcome, <?php echo htmlspecialchars($user['username']); ?>!</h2>

<p>Email: <?php echo htmlspecialchars($user['email']); ?></p>
<p>Member since: <?php echo htmlspecialchars($user['created_at']); ?></p>

<?php
require 'templates/footer.php';
?>

10. User Logout (logout.php)

This script logs the user out by destroying the session.

<?php
// logout.php

session_start();
session_unset();
session_destroy();

// Redirect to login page
header('Location: login.php');
exit;
?>

11. Testing the Application

  1. Access the Registration Page:
    • Navigate to http://localhost/user-auth/register.php.
    • Fill in the registration form and submit.
    • Verify that the user is registered and redirected appropriately.
  2. Access the Login Page:
    • Navigate to http://localhost/user-auth/login.php.
    • Enter the registered credentials and log in.
    • Verify access to the dashboard.
  3. Access Protected Page:
    • Navigate to http://localhost/user-auth/dashboard.php.
    • Ensure that only logged-in users can access this page.
  4. Logout:
    • Click on “Logout”.
    • Verify that the session is destroyed and access to the dashboard is restricted.

12. Deployment Considerations

When deploying to a live server:

  • Secure Database Credentials: Use environment variables or secure configuration methods.
  • Use HTTPS: Ensure your website uses HTTPS for encrypted data transmission.
  • Regular Backups: Implement regular backups of your database and files.
  • Error Reporting: Disable detailed error messages in production to prevent information leakage. // In production, set error reporting to minimal ini_set('display_errors', 0); ini_set('log_errors', 1);
  • Update Dependencies: Keep PHP and all libraries updated to their latest versions to patch security vulnerabilities.
  • Implement Strong Password Policies: Enforce strong passwords and consider adding features like password reset via email.
Leave a Comment

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply

Your email address will not be published. Required fields are marked *