User authentication is one of the most important parts of any web application. While creating a login and registration system in PHP is quite common, many developers forget to implement a secure Forgot Password feature.
A Forgot Password system allows users to reset their password in case they lose access. In this article, we’ll build a secure Forgot Password system using PHP, AJAX, and MySQL with email verification.
Workflow of Forgot Password in PHP
- User submits email in the Forgot Password form.
- System checks if the email exists in the users table.
- A unique reset token is generated and stored in the database with expiry time.
- An email with a reset link is sent to the user.
- The user clicks the link and sets a new password.
- The system verifies the token, updates the password, and clears the reset token.
Database Table Example (MySQL)
CREATE TABLE users ( id INT AUTO_INCREMENT PRIMARY KEY, username VARCHAR(100), email VARCHAR(100) UNIQUE, password VARCHAR(255), reset_token VARCHAR(255) NULL, reset_expiry DATETIME NULL );
This table stores user data and extra columns reset_token and reset_expiry for password reset.
Forgot Password Form (HTML + AJAX)
<form id="formForgotPassword">
<label>Email:</label>
<input type="email" name="email" required />
<button type="submit">Send Reset Link</button>
</form>
<div id="responseMessage"></div>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script>
$("#formForgotPassword").on("submit", function(e) {
e.preventDefault();
$.ajax({
url: "forgot_password_ajax.php",
type: "POST",
data: $(this).serialize(),
success: function(response) {
$("#responseMessage").html(response);
},
error: function() {
$("#responseMessage").html("AJAX Error Occurred.");
}
});
});
</script>
Backend: Send Reset Link (PHP + PHPMailer)
<?php
include 'db.php';
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;
require 'vendor/autoload.php';
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$email = $_POST['email'];
$stmt = $conn->prepare("SELECT * FROM users WHERE email=?");
$stmt->bind_param("s", $email);
$stmt->execute();
$result = $stmt->get_result();
if($result->num_rows > 0) {
$token = bin2hex(random_bytes(32));
$expiry = date("Y-m-d H:i:s", strtotime('+1 hour'));
$update = $conn->prepare("UPDATE users SET reset_token=?, reset_expiry=? WHERE email=?");
$update->bind_param("sss", $token, $expiry, $email);
$update->execute();
$resetLink = "http://localhost/reset_password.php?token=".$token;
$mail = new PHPMailer(true);
$mail->isSMTP();
$mail->Host = "smtp.gmail.com";
$mail->SMTPAuth = true;
$mail->Username = "yourgmail@gmail.com";
$mail->Password = "your_app_password";
$mail->SMTPSecure = "tls";
$mail->Port = 587;
$mail->setFrom("yourgmail@gmail.com", "Your App");
$mail->addAddress($email);
$mail->isHTML(true);
$mail->Subject = "Reset Your Password";
$mail->Body = "Click this link to reset your password: <a href='$resetLink'>$resetLink</a>";
$mail->send();
echo "<span style='color:green;'>Reset link sent to your email.</span>";
} else {
echo "<span style='color:red;'>Email not found!</span>";
}
}
?>
Reset Password Page
<?php
include 'db.php';
if (isset($_GET['token'])) {
$token = $_GET['token'];
$stmt = $conn->prepare("SELECT * FROM users WHERE reset_token=? AND reset_expiry > NOW()");
$stmt->bind_param("s", $token);
$stmt->execute();
$result = $stmt->get_result();
if($result->num_rows > 0) {
echo '
<form id="resetForm">
<input type="hidden" name="token" value="'.$token.'">
<label>New Password:</label>
<input type="password" name="password" required />
<button type="submit">Reset Password</button>
</form>
<div id="resetMessage"></div>
';
} else {
echo "Invalid or expired link.";
}
}
?>
Reset Password via AJAX
<?php
include 'db.php';
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$token = $_POST['token'];
$password = password_hash($_POST['password'], PASSWORD_BCRYPT);
$stmt = $conn->prepare("SELECT * FROM users WHERE reset_token=? AND reset_expiry > NOW()");
$stmt->bind_param("s", $token);
$stmt->execute();
$result = $stmt->get_result();
if($result->num_rows > 0) {
$update = $conn->prepare("UPDATE users SET password=?, reset_token=NULL, reset_expiry=NULL WHERE reset_token=?");
$update->bind_param("ss", $password, $token);
$update->execute();
echo "<span style='color:green;'>Password updated successfully! <a href='login.php'>Login</a></span>";
} else {
echo "<span style='color:red;'>Invalid or expired token.</span>";
}
}
?>
Final Thoughts
With this system, we’ve successfully built a Forgot Password feature in PHP with AJAX and MySQL.
- Users can reset their password securely.
- Emails are sent using SMTP (PHPMailer).
- The token system ensures passwords can only be reset via a secure link.
This approach improves both security and user experience for your PHP application.
