How to Create a Secure Login Page with Password Reset Functions using Bootstrap 5.3
In this tutorial, we will guide you step by step on how to create a login page with various features using the Bootstrap 5.3 framework. The login page will include options for login, registration, and password recovery. We will also cover how to set up a database, establish a connection using a configuration file, and store user data securely.
Prerequisites: Before we begin, make sure you have the following:
- Basic knowledge of HTML, CSS, and PHP.
- A web server with PHP support.
- MySQL database server.
- Bootstrap 5.3 framework (including CSS and JS files) integrated into your project.
Setting up the Database
Create a new MySQL database and a table to store user information. Include fields for timestamp, email, first name, last name, display name, and password. (display name not required) You can execute the following SQL query using a tool like phpMyAdmin:
-- Create the database
CREATE DATABASE login_system;
-- Use the database
USE login_system;
-- Create the users table
CREATE TABLE users (
id INT PRIMARY KEY AUTO_INCREMENT,
email VARCHAR(255) NOT NULL,
password VARCHAR(255) NOT NULL,
first_name VARCHAR(50) NOT NULL,
last_name VARCHAR(50) NOT NULL,
display_name VARCHAR(50) NOT NULL,
reset_code VARCHAR(50),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Create an index on the email column for faster lookup
CREATE INDEX idx_email ON users (email);
Creating the Configuration File
Create a separate PHP file, such as config.php
, to store your database configuration details. Modify the file with your specific database credentials:
<?php
$host = 'localhost';
$db_name = 'your_database_name';
$username = 'your_username';
$password = 'your_password';
// Establish the database connection
try {
$conn = new PDO("mysql:host=$host;dbname=$db_name", $username, $password);
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
die("Connection failed: " . $e->getMessage());
}
?>
Remember to keep the config.php
file outside the web root directory or restrict its access to ensure its security.
Creating the Index or Login Page
First, create an HTML file for the login page called index.php. Include the necessary Bootstrap 5.3 CSS and JS files. Use the following code as a starting point for index.php:
In this code, the combined index.php
and login.php
files, which are normally separate, first checks if the user is already logged in using isset($_SESSION['user_id'])
. If the user is logged in, it immediately redirects to the database.php
page.
<?php
require 'config.php';
// Initialize the session
session_start();
// Check if the user is already logged in
if (isset($_SESSION['user_id'])) {
header('Location: database.php');
exit();
}
// Handle the login form submission
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$email = $_POST['email'];
$password = $_POST['password'];
// Retrieve user from the database based on email
$stmt = $conn->prepare("SELECT * FROM users WHERE email = ?");
$stmt->execute([$email]);
$user = $stmt->fetch();
// Check if the user exists and verify the password
if ($user && password_verify($password, $user['password'])) {
// Set the user ID in the session
$_SESSION['user_id'] = $user['id'];
// Redirect to the database page
header('Location: database.php');
exit();
} else {
$error_message = 'Invalid email or password.';
}
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Login</title>
<!-- Include Bootstrap CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM" crossorigin="anonymous">
</head>
<body>
<div class="container">
<h2>Login</h2>
<?php if (isset($error_message)) : ?>
<div class="alert alert-danger" role="alert">
<?php echo $error_message; ?>
</div>
<?php endif; ?>
<form action="index.php" method="POST">
<div class="mb-3">
<label for="email">Email:</label>
<input type="email" name="email" id="email" class="form-control" value="<?php echo isset($error_message) ? htmlspecialchars($email) : ''; ?>" required>
</div>
<div class="mb-3">
<label for="password">Password:</label>
<input type="password" name="password" id="password" class="form-control" required>
</div>
<button type="submit" class="btn btn-primary">Login</button>
</form>
<p class="mt-3">
<a href="register.php">Register</a> | <a href="forgot_password.php">Forgot Password</a>
</p>
</div>
<!-- Include Bootstrap JS -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.min.js" integrity="sha384-fbbOQedDUMZZ5KreZpsbe1LCZPVmfTnH7ois6mU1QK+m14rQ1l2bGBq41eYeM/fS" crossorigin="anonymous"></script>
</body>
</html>
Upon submitting the login form ($_SERVER['REQUEST_METHOD'] === 'POST'
), it retrieves the provided email and password. It then checks if the email exists in the database and verifies the password using password_verify()
.
Authentication:
If the authentication is successful, it sets the user_id
in the $_SESSION
variable and redirects to the database.php
page.
If authentication fails, it adds an error message to the $errors
array using Bootstrap’s alert component within the HTML structure.
Please ensure that you have a database.php
page where you want to redirect the user upon successful authentication. Modify the header('Location: database.php')
line if the actual path or filename differs.
Creating the Registration Page
Create an HTML file, such as register.php
, for the user registration page. Include the necessary Bootstrap files. This registration form should collect relevant user details such as email, first name, last name, display name, and password. Use the following code as a starting point:
<?php
require 'config.php';
session_start();
// Check if the user is already logged in
if (isset($_SESSION['user_id'])) {
header('Location: database.php');
exit();
}
$errors = [];
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$email = $_POST['email'];
$first_name = $_POST['first_name'];
$last_name = $_POST['last_name'];
$display_name = $_POST['display_name'];
$password = $_POST['password'];
$confirmPassword = $_POST['confirm_password'];
// Validate the form data
if (empty($email) || empty($first_name) || empty($last_name) || empty($display_name) || empty($password) || empty($confirmPassword)) {
$errors[] = 'Please fill in all the fields.';
} elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$errors[] = 'Invalid email format.';
} else {
// Check if the email already exists in the database
$stmt = $conn->prepare("SELECT * FROM users WHERE email = ?");
$stmt->execute([$email]);
$user = $stmt->fetch();
if ($user) {
$errors[] = 'Email is already registered.';
} elseif ($password !== $confirmPassword) {
$errors[] = "Passwords do not match";
} else {
// Insert user into the database
$password = password_hash($password, PASSWORD_DEFAULT);
$stmt = $conn->prepare("INSERT INTO users (email, first_name, last_name, display_name, password) VALUES (?, ?, ?, ?, ?)");
$stmt->execute([$email, $first_name, $last_name, $display_name, $password]);
// Log the user into the database
$user_id = $conn->lastInsertId();
$_SESSION['user_id'] = $user_id;
// Redirect to the database page
header('Location: database.php');
exit();
}
}
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Registration</title>
<!-- Include Bootstrap CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM" crossorigin="anonymous">
</head>
<body>
<div class="container">
<h2>Registration</h2>
<?php if (!empty($errors)) : ?>
<div class="alert alert-danger" role="alert">
<?php foreach ($errors as $error) : ?>
<p><?php echo $error; ?></p>
<?php endforeach; ?>
</div>
<?php endif; ?>
<form action="register.php" method="POST">
<div class="mb-3">
<label for="email">Email:</label>
<input type="email" name="email" id="email" class="form-control" value="<?php echo isset($errors) ? htmlspecialchars($email) : ''; ?>" required>
</div>
<div class="mb-3">
<label for="first_name">First Name:</label>
<input type="text" name="first_name" id="first_name" class="form-control" value="<?php echo isset($errors) ? htmlspecialchars($first_name) : ''; ?>" required>
</div>
<div class="mb-3">
<label for="last_name">Last Name:</label>
<input type="text" name="last_name" id="last_name" class="form-control" value="<?php echo isset($errors) ? htmlspecialchars($last_name) : ''; ?>" required>
</div>
<div class="mb-3">
<label for="display_name">Display Name:</label>
<input type="text" name="display_name" id="display_name" class="form-control" value="<?php echo isset($errors) ? htmlspecialchars($display_name) : ''; ?>" required>
</div>
<div class="mb-3">
<label for="password">Password:</label>
<input type="password" name="password" id="password" class="form-control" required>
</div>
<div class="mb-3">
<label for="confirm_password">Confirm Password:</label>
<input type="password" name="confirm_password" id="confirm_password" class="form-control" required>
</div>
<button type="submit" class="btn btn-primary">Register</button>
</form>
</div>
<!-- Include Bootstrap JS -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.min.js" integrity="sha384-fbbOQedDUMZZ5KreZpsbe1LCZPVmfTnH7ois6mU1QK+m14rQ1l2bGBq41eYeM/fS" crossorigin="anonymous"></script>
</body>
</html>
Creating the Forgot Password Page
Create an HTML file, such as forgot_password.php
, for the password recovery page. Include the necessary Bootstrap files. The page should collect the user’s email address to initiate the password reset process.
Implement a password reset functionality by sending a reset link to the user’s email address. This process involves generating a unique token, storing it in the database alongside the user’s email, and sending an email with the reset link.
Handling Password Reset
create the forget_password.php file.
Add the following necessary validation for the email and reset code in the forgot_password.php
file.
<?php
session_start();
// Check if the user is already logged in
if (isset($_SESSION['user_id'])) {
header('Location: database.php');
exit();
}
// Check if the form is submitted
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// Retrieve the submitted email
$email = $_POST['email'];
// Validate the email
if (filter_var($email, FILTER_VALIDATE_EMAIL)) {
// Generate a reset code
$resetCode = generateResetCode();
// Store the reset code in session
$_SESSION['reset_code'] = $resetCode;
// Send the reset code to the user's email
$subject = 'Password Reset Code';
$message = "Your password reset code is: $resetCode";
$headers = "From: support@selfhelpthatmatters.com\r\n";
$headers .= "Reply-To: support@selfhelpthatmatters.com\r\n";
$headers .= "To: $email\r\n";
if (mail($email, $subject, $message, $headers)) {
// Email sent successfully
header('Location: reset_password.php');
exit();
} else {
// Failed to send email
$error = "Failed to send reset code. Please try again later.";
}
// Example code using PHPMailer incase you have issues with regular mail() command:
/*
require 'PHPMailer/PHPMailer.php';
require 'PHPMailer/SMTP.php';
require 'PHPMailer/Exception.php';
$mail = new PHPMailer\PHPMailer\PHPMailer();
$mail->isSMTP();
$mail->Host = 'smtp.example.com';
$mail->SMTPAuth = true;
$mail->Username = 'your-email@example.com';
$mail->Password = 'your-email-password';
$mail->SMTPSecure = 'tls';
$mail->Port = 587;
$mail->setFrom('your-email@example.com', 'Your Name');
$mail->addAddress($email);
$mail->Subject = 'Password Reset Code';
$mail->Body = "Your password reset code is: $resetCode";
if ($mail->send()) {
// Email sent successfully
return true;
} else {
// Email sending failed
return false;
}
*/
} else {
$error = "Invalid email format";
}
}
// Function to generate a random reset code
function generateResetCode() {
$length = 8;
$characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
$code = '';
for ($i = 0; $i < $length; $i++) {
$code .= $characters[rand(0, strlen($characters) - 1)];
}
return $code;
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Forgot Password</title>
<!-- Include Bootstrap CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM" crossorigin="anonymous">
</head>
<body>
<div class="container">
<h2>Forgot Password</h2>
<?php if (isset($error)) : ?>
<div class="alert alert-danger" role="alert">
<?php echo $error; ?>
</div>
<?php elseif (isset($_GET['success'])) : ?>
<div class="alert alert-success" role="alert">
Reset code sent to your email. Check your inbox.
</div>
<?php endif; ?>
<form action="" method="POST">
<div class="mb-3">
<label for="email">Email:</label>
<input type="email" name="email" id="email" class="form-control" required>
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
</div>
<!-- Include Bootstrap JS -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.min.js" integrity="sha384-fbbOQedDUMZZ5KreZpsbe1LCZPVmfTnH7ois6mU1QK+m14rQ1l2bGBq41eYeM/fS" crossorigin="anonymous"></script>
</body>
</html>
Remember to adjust the email content, replace example.com
with your own domain, and handle the password reset process in a separate reset_password.php
file, where users can enter a new password based on the email and token combination.
<?php
session_start();
// Check if the reset code exists in the session
if (!isset($_SESSION['reset_code'])) {
header('Location: forgot_password.php');
exit();
}
// Check if the form is submitted
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// Retrieve the submitted reset code and new password
$resetCode = $_SESSION['reset_code'];
$newPassword = $_POST['new_password'];
// Validate the reset code (you can add your own validation logic here)
if ($_POST['reset_code'] === $resetCode) {
// TODO: Update the user's password in the database using the new password
// Password updated successfully
header('Location: index.php?success=reset');
exit();
} else {
$error = "Invalid reset code";
}
}
// Clear the reset code from session if reset code validation fails
unset($_SESSION['reset_code']);
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Reset Password</title>
<!-- Include Bootstrap CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM" crossorigin="anonymous">
</head>
<body>
<div class="container">
<h2>Reset Password</h2>
<?php if (isset($error)) : ?>
<div class="alert alert-danger" role="alert">
<?php echo $error; ?>
</div>
<?php endif; ?>
<form action="" method="POST">
<div class="mb-3">
<label for="reset_code">Reset Code:</label>
<input type="text" name="reset_code" id="reset_code" class="form-control" required>
</div>
<div class="mb-3">
<label for="new_password">New Password:</label>
<input type="password" name="new_password" id="new_password" class="form-control" required>
</div>
<button type="submit" class="btn btn-primary">Reset Password</button>
</form>
</div>
<!-- Include Bootstrap JS -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.min.js" integrity="sha384-fbbOQedDUMZZ5KreZpsbe1LCZPVmfTnH7ois6mU1QK+m14rQ1l2bGBq41eYeM/fS" crossorigin="anonymous"></script>
</body>
</html>
Create the database.php file
In this updated database.php
file, the user’s authentication is checked, and their details are retrieved from the database using the provided config.php
file. The user’s ID stored in the session variable is used to fetch their information from the users
table.
The logout button is provided at the bottom to allow the user to log out.
Please make sure to adjust the session variable names (user_id
) and the database table name (users
) based on your implementation.
Remember to adjust the file names and paths according to your project structure.
<?php
session_start();
// Check if the user is not logged in
if (!isset($_SESSION['user_id'])) {
header('Location: index.php');
exit();
}
// Retrieve user details from the database
$userID = $_SESSION['user_id'];
require_once 'config.php';
try {
$stmt = $conn->prepare('SELECT email, first_name, last_name, display_name FROM users WHERE id = :id');
$stmt->bindParam(':id', $userID);
$stmt->execute();
$user = $stmt->fetch(PDO::FETCH_ASSOC);
// If the user does not exist in the database, redirect to login page
if (!$user) {
header('Location: index.php');
exit();
}
} catch (PDOException $e) {
die('Database error: ' . $e->getMessage());
}
$email = $user['email'];
$firstName = $user['first_name'];
$lastName = $user['last_name'];
$displayName = $user['display_name'];
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Database Page</title>
<!-- Include Bootstrap CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM" crossorigin="anonymous">
</head>
<body>
<div class="container">
<h2>Welcome, <?php echo $displayName; ?>!</h2>
<p>Email: <?php echo $email; ?></p>
<p>First Name: <?php echo $firstName; ?></p>
<p>Last Name: <?php echo $lastName; ?></p>
<h4>This is a protected page that can only be accessed by authenticated users.</h4>
<a href="logout.php" class="btn btn-primary">Logout</a>
</div>
<!-- Include Bootstrap JS -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.min.js" integrity="sha384-fbbOQedDUMZZ5KreZpsbe1LCZPVmfTnH7ois6mU1QK+m14rQ1l2bGBq41eYeM/fS" crossorigin="anonymous"></script>
</body>
</html>
To complete the login application, let’s create the logout.php
file, which will handle the logout functionality and redirect the user to the index.php
page:
<?php
session_start();
// Clear all session variables
$_SESSION = array();
// Destroy the session
session_destroy();
// Redirect to the login page
header('Location: index.php');
exit();
?>
In the logout.php
file, the session is cleared by emptying the $_SESSION
array, and then the session is destroyed using session_destroy()
.
Finally, the user is redirected to the index.php
page, which is the login page.
With this implementation, when the user clicks on the logout button in the database.php
page, they will be logged out, and upon successful logout, they will be redirected to the login page.
Remember to adjust the file names and paths according to your project structure.
Congratulations! You have now successfully created a feature-rich login system with registration, password reset, and database access.
Play with this basic login form here:
https://selfhelpthatmatters.com/mockups/bootstrap5-3login-with-password-reset-fully-functional/