A secure forgot password feature boosts user trust and reduces support tickets. Implementing OTP (one-time password) email verification with PHP ensures only the true account owner can reset the password, adding an extra security layer to your site.
In this tutorial, youโll learn how to implement a Forgot Password system in PHP that verifies users through an Email OTP (One-Time Password) before allowing them to reset their password.
This is a secure and modern way to let users recover their account access when they forget their password.
Features:
Technologies Used:
Before getting started to integrate the Forgot Password System with PHP, take a look at the file structure for this tutorial:
forgot-password-with-email-otp/ โโโ config.php โโโ forgot-password.php โโโ verify-otp.php โโโ reset-password.php โโโ style.css โโโ PHPMailer/
This tutorial uses the PHPMailer library to send OTP emails. You can download it from its official GitHub repository or install it via Composer:
composer require phpmailer/phpmailer
Note: All the required PHPMailer files are already included in our ready-to-download source code ZIP package. You don’t need to download or install it separately.
Create a MySQL database (e.g., user_auth_db) and a users table to store user information, including email, password, and created_at timestamp.
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
email VARCHAR(255) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
Create a config.php file to store database connection and SMTP email settings.
$dbHost, $dbUsername, $dbPassword, $dbName) with your database credentials.$smtpHost, $smtpUsername, $smtpPassword, $smtpPort, $smtpFromEmail, $smtpFromName) with your SMTP server details.<?php
// Database configuration
$dbHost = "localhost";
$dbUsername = "root";
$dbPassword = "root_pass";
$dbName = "user_auth_db";
// Create database connection
$conn = new mysqli($dbHost, $dbUsername, $dbPassword, $dbName);
// Check connection
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
// Email configuration
$smtpHost = 'your.smtp.server';
$smtpUsername = 'your@email.com';
$smtpPassword = 'your-smtp-password';
$smtpPort = 587;
$smtpFromEmail = 'noreply@yourdomain.com';
$smtpFromName = 'Your App';
// Start session
if(session_status() == PHP_SESSION_NONE){
session_start();
}
?>
Create a forgot-password.php file with a form to collect the user’s email address.
The PHP code at the top of the file handles the following:
verify-otp.php) upon successful email sending.<?php
// Include configuration file
require_once 'config.php';
// Import PHPMailer classes into the global namespace
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;
require 'PHPMailer/Exception.php';
require 'PHPMailer/PHPMailer.php';
require 'PHPMailer/SMTP.php';
// Handle form submission
if ($_SERVER["REQUEST_METHOD"] == "POST") {
$email = $_POST['email'];
// Check if email exists in database
$stmt = $conn->prepare("SELECT id FROM users WHERE email = ?");
$stmt->bind_param("s", $email);
$stmt->execute();
$result = $stmt->get_result();
if ($result->num_rows > 0) {
// Generate OTP
$otp = rand(100000, 999999);
$_SESSION['reset_otp'] = $otp;
$_SESSION['reset_email'] = $email;
$_SESSION['otp_time'] = time();
// Create a new PHPMailer instance
$mail = new PHPMailer(true);
try {
// Server settings
$mail->isSMTP();
$mail->Host = $smtpHost;
$mail->SMTPAuth = true;
$mail->Username = $smtpUsername;
$mail->Password = $smtpPassword;
$mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
$mail->Port = $smtpPort;
// Recipients
$mail->setFrom($smtpFromEmail, $smtpFromName);
$mail->addAddress($email);
// Content
$mail->isHTML(true);
$mail->Subject = 'Password Reset OTP';
$mail->Body = "Your OTP for password reset is: <b>$otp</b><br>This OTP will expire in 10 minutes.";
if ($mail->send()) {
// Set a session status message so verify-otp.php can show it after redirect
$_SESSION['status'] = "An OTP has been sent to your email address. Please check your inbox (and spam).";
header("Location: verify-otp.php");
exit();
} else {
$error = "Message could not be sent. Mailer Error: {$mail->ErrorInfo}";
}
} catch (Exception $e) {
$error = "Message could not be sent. Mailer Error: {$mail->ErrorInfo}";
}
} else {
$error = "Email address not found!";
}
}
?>
<form method="POST" class="form">
<h2>Forgot Password</h2>
<?php if (isset($error)) { ?>
<div class="error"><?php echo $error; ?></div>
<?php } ?>
<div class="form-group">
<label for="email">Email Address</label>
<input type="email" name="email" id="email" required>
</div>
<button type="submit">Send OTP</button>
<div class="links">
<a href="login.php">Back to Login</a>
</div>
</form>
Create a verify-otp.php file with a form to collect the OTP from the user.
The PHP code at the top of the file handles the following:
reset-password.php).<?php
// Include configuration file
require_once 'config.php';
// Check if OTP and email are set in session
if (!isset($_SESSION['reset_otp']) || !isset($_SESSION['reset_email'])) {
header("Location: forgot-password.php");
exit();
}
// Capture and clear any status message set by previous step
$status = null;
if (isset($_SESSION['status'])) {
$status = $_SESSION['status'];
unset($_SESSION['status']);
}
// Handle form submission
if ($_SERVER["REQUEST_METHOD"] == "POST") {
$entered_otp = $_POST['otp'];
$stored_otp = $_SESSION['reset_otp'];
$otp_time = $_SESSION['otp_time'];
// Check if OTP is expired (10 minutes validity)
if (time() - $otp_time > 600) {
$error = "OTP has expired. Please request a new one.";
unset($_SESSION['reset_otp']);
unset($_SESSION['otp_time']);
}
// Verify OTP
else if ($entered_otp == $stored_otp) {
$_SESSION['otp_verified'] = true;
header("Location: reset-password.php");
exit();
} else {
$error = "Invalid OTP. Please try again.";
}
}
?>
<form method="POST" class="form">
<h2>Verify OTP</h2>
<?php if (isset($error)) { ?>
<div class="error"><?php echo $error; ?></div>
<?php } ?>
<?php if (isset($status)) { ?>
<div class="success"><?php echo $status; ?></div>
<?php } ?>
<div class="form-group">
<label for="otp">Enter OTP</label>
<input type="number" name="otp" id="otp" required>
</div>
<button type="submit">Verify OTP</button>
<div class="links">
<a href="forgot-password.php">Request New OTP</a>
</div>
</form>
Create a reset-password.php file with a form to allow the user to reset their password.
The PHP code at the top of the file handles the following:
<?php
// Include configuration file
require_once 'config.php';
// Check if OTP is verified
if (!isset($_SESSION['otp_verified']) || !isset($_SESSION['reset_email'])) {
header("Location: forgot-password.php");
exit();
}
// Handle form submission
if ($_SERVER["REQUEST_METHOD"] == "POST") {
$password = $_POST['password'];
$confirm_password = $_POST['confirm_password'];
if ($password !== $confirm_password) {
$error = "Passwords do not match!";
} else {
$email = $_SESSION['reset_email'];
$hashed_password = password_hash($password, PASSWORD_DEFAULT);
// Update password in database
$stmt = $conn->prepare("UPDATE users SET password = ? WHERE email = ?");
$stmt->bind_param("ss", $hashed_password, $email);
if ($stmt->execute()) {
// Clear all session variables
session_unset();
session_destroy();
header("Location: login.php?reset=success");
exit();
} else {
$error = "Error updating password. Please try again.";
}
}
}
?>
<form method="POST" class="form">
<h2>Reset Password</h2>
<?php if (isset($error)) { ?>
<div class="error"><?php echo $error; ?></div>
<?php } ?>
<div class="form-group">
<label for="password">New Password</label>
<input type="password" name="password" id="password" required>
</div>
<div class="form-group">
<label for="confirm_password">Confirm Password</label>
<input type="password" name="confirm_password" id="confirm_password" required>
</div>
<button type="submit">Reset Password</button>
</form>
You have successfully created a Forgot Password with Email OTP Verification system using PHP and MySQL. This secure approach ensures only verified users can reset their passwords and helps prevent unauthorized access.
This script can be further enhanced by integrating it into a complete user authentication system. For a full registration and login system, you can refer to this comprehensive tutorial: Secure Registration and Login System with PHP and MySQL
Make sure to test the entire flow thoroughly and adapt the code to fit your application’s structure and security requirements. Happy coding! ๐
Looking for expert assistance to implement or extend this scriptโs functionality? Submit a Service Request
๐ฐ Budget-friendly โข ๐ Global clients โข ๐ Production-ready solutions