Overview
The QR Attendance System includes a password reset feature that allows students and teachers to recover their accounts. The current implementation uses a token-based system that can be extended with email functionality.
Password Reset Flow
The password reset process involves three main components:
Request password reset
User enters their ID and email on the forgot password page (forgot_password.php)
Verify and generate token
System verifies credentials and generates a secure reset token (reset_password.php)
Set new password
User creates a new password using the reset form (password_reset_form.php and update_password.php)
The forgot password interface collects user credentials to initiate the reset process.
forgot_password.php:38-48
<form method="post" action="reset_password.php">
<div class="form-group">
<label>User ID</label>
<input type="text" name="userID" required placeholder="Enter your user ID">
</div>
<div class="form-group">
<label>Email</label>
<input type="email" name="email" required placeholder="Enter your registered email">
</div>
<button type="submit" class="btn">Reset Password</button>
</form>
Token Generation
User Verification
The system first verifies the user exists with the provided credentials:
// Check if it's a student (numeric ID)
if (ctype_digit($userID)) {
$stmt = $pdo->prepare("SELECT * FROM students WHERE student_id = ? AND email = ?");
$stmt->execute([$userID, $email]);
$user = $stmt->fetch();
$userType = 'student';
}
// Check if it's a teacher (ID starts with T)
else if (strpos($userID, "T") === 0) {
$stmt = $pdo->prepare("SELECT * FROM teachers WHERE teacher_id = ? AND email = ?");
$stmt->execute([$userID, $email]);
$user = $stmt->fetch();
$userType = 'teacher';
}
// Invalid ID format
else {
$_SESSION['error'] = "Invalid user ID format.";
header("Location: forgot_password.php");
exit();
}
The system automatically detects user type:
- Students: Numeric IDs (e.g.,
12345)
- Teachers: IDs starting with “T” (e.g.,
T1234)
Secure Token Creation
When credentials are verified, a cryptographically secure token is generated:
// Generate a random token
$token = bin2hex(random_bytes(32));
$expiry = date('Y-m-d H:i:s', strtotime('+1 hour'));
// Store the reset token in the database
$tableName = ($userType === 'student') ? 'students' : 'teachers';
$idField = ($userType === 'student') ? 'student_id' : 'teacher_id';
$stmt = $pdo->prepare("UPDATE $tableName SET reset_token = ?, reset_token_expiry = ? WHERE $idField = ?");
$stmt->execute([$token, $expiry, $userID]);
Token Security Features:
- 64-character hexadecimal token (256 bits of entropy)
- 1-hour expiration window
- Stored securely in database
- Single-use (cleared after password update)
Session Storage
The token and user details are stored in the session for the reset form:
$_SESSION['reset_token'] = $token;
$_SESSION['reset_user_id'] = $userID;
$_SESSION['reset_user_type'] = $userType;
header("Location: password_reset_form.php");
Session Validation
The reset form validates that a valid reset session exists:
password_reset_form.php:4-9
if (!isset($_SESSION['reset_token']) || !isset($_SESSION['reset_user_id']) || !isset($_SESSION['reset_user_type'])) {
$_SESSION['error'] = "Invalid password reset session.";
header("Location: forgot_password.php");
exit();
}
The form includes hidden fields to maintain reset context:
password_reset_form.php:45-59
<form method="post" action="update_password.php">
<input type="hidden" name="token" value="<?php echo htmlspecialchars($token); ?>">
<input type="hidden" name="userID" value="<?php echo htmlspecialchars($userID); ?>">
<input type="hidden" name="userType" value="<?php echo htmlspecialchars($userType); ?>">
<div class="form-group">
<label>New Password</label>
<input type="password" name="new_password" required placeholder="Enter new password" minlength="8">
</div>
<div class="form-group">
<label>Confirm Password</label>
<input type="password" name="confirm_password" required placeholder="Confirm new password" minlength="8">
</div>
<button type="submit" class="btn">Update Password</button>
</form>
All hidden fields use htmlspecialchars() to prevent XSS attacks.
Password Update Process
Validation
The update process performs multiple security checks:
update_password.php:11-23
// Validate passwords match
if ($newPassword !== $confirmPassword) {
$_SESSION['error'] = "Passwords do not match.";
header("Location: password_reset_form.php");
exit();
}
// Validate password strength
if (strlen($newPassword) < 8) {
$_SESSION['error'] = "Password must be at least 8 characters long.";
header("Location: password_reset_form.php");
exit();
}
Token Verification
The system verifies the token is valid and not expired:
update_password.php:29-32
$stmt = $pdo->prepare("SELECT * FROM $tableName WHERE $idField = ? AND reset_token = ? AND reset_token_expiry > NOW()");
$stmt->execute([$userID, $token]);
$user = $stmt->fetch();
The reset_token_expiry > NOW() condition ensures expired tokens are rejected automatically.
Password Update and Cleanup
If the token is valid, the password is updated and reset data is cleared:
update_password.php:34-49
if ($user) {
// Hash the new password
$hashedPassword = password_hash($newPassword, PASSWORD_DEFAULT);
// Update the password and clear the reset token
$stmt = $pdo->prepare("UPDATE $tableName SET password = ?, reset_token = NULL, reset_token_expiry = NULL WHERE $idField = ?");
$stmt->execute([$hashedPassword, $userID]);
// Clear the reset session
unset($_SESSION['reset_token']);
unset($_SESSION['reset_user_id']);
unset($_SESSION['reset_user_type']);
$_SESSION['success'] = "Your password has been updated successfully. You can now login with your new password.";
header("Location: index.php");
exit();
}
Email Integration
The current implementation uses session-based password reset. To add email functionality:
Step 1: Install Email Library
Install PHPMailer or use PHP’s built-in mail() function:
composer require phpmailer/phpmailer
Add email configuration to config.php:
// Email configuration
$smtp_host = 'smtp.gmail.com';
$smtp_port = 587;
$smtp_username = '[email protected]';
$smtp_password = 'your-app-password';
$from_email = '[email protected]';
$from_name = 'QR Attendance System';
Step 3: Send Reset Email
Replace the session redirect in reset_password.php:42-48 with:
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;
require 'vendor/autoload.php';
$mail = new PHPMailer(true);
try {
// SMTP configuration
$mail->isSMTP();
$mail->Host = $smtp_host;
$mail->SMTPAuth = true;
$mail->Username = $smtp_username;
$mail->Password = $smtp_password;
$mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
$mail->Port = $smtp_port;
// Email content
$mail->setFrom($from_email, $from_name);
$mail->addAddress($email);
$mail->Subject = 'Password Reset Request';
$reset_link = "https://yourdomain.com/password_reset_form.php?token=$token&user=$userID&type=$userType";
$mail->isHTML(true);
$mail->Body = "
<h2>Password Reset Request</h2>
<p>Click the link below to reset your password:</p>
<a href='$reset_link'>Reset Password</a>
<p>This link will expire in 1 hour.</p>
<p>If you didn't request this, please ignore this email.</p>
";
$mail->send();
$_SESSION['success'] = "Password reset link sent to your email.";
} catch (Exception $e) {
$_SESSION['error'] = "Failed to send email: {$mail->ErrorInfo}";
}
Modify password_reset_form.php to accept token from URL parameters:
$token = $_GET['token'] ?? $_SESSION['reset_token'] ?? null;
$userID = $_GET['user'] ?? $_SESSION['reset_user_id'] ?? null;
$userType = $_GET['type'] ?? $_SESSION['reset_user_type'] ?? null;
Always validate URL parameters to prevent token manipulation attacks.
Security Best Practices
- Use HTTPS: Always send reset emails over encrypted connections
- Rate Limiting: Implement request throttling to prevent abuse
- Email Verification: Verify email addresses during registration
- Audit Logging: Log all password reset attempts
- Token Cleanup: Periodically delete expired tokens from the database
Testing Password Reset
To test the current implementation:
Create a test account
Register as a student or teacher
Access forgot password
Go to forgot_password.php and enter your ID and email
Reset password
You’ll be redirected to the password reset form. Enter a new password.
Login
Return to the login page and authenticate with your new password