Project Overview
Two-Factor Authentication (2FA) adds an extra layer of security by requiring users to provide two forms of identification before accessing their accounts. This project covers integrating 2FA using authenticator apps (like Google Authenticator) with PHP and MySQL, enhancing the security of user authentication systems.
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
- Google Authenticator App: Installed on a smartphone
- Basic Understanding of Sessions, Forms, and QR Code Generation in PHP
Step-by-Step Procedure
1. Setting Up the Development Environment
- Install XAMPP:
- Download from XAMPP Official Website.
- Follow the installation wizard and install it in the default directory.
- Start Apache and MySQL:
- Open the XAMPP Control Panel.
- Start the Apache and MySQL modules.
- Install Composer:
- Download from Composer Official Website.
- Follow the installation instructions for your operating system.
2. Creating the Database
- Access phpMyAdmin:
- Navigate to
http://localhost/phpmyadmin/
in your browser.
- Navigate to
- Create a New Database:
- Click on “New” in the left sidebar.
- Name the database
two_factor_auth
. - Choose “utf8mb4_unicode_ci” as the collation.
- Click “Create”.
- Create a
users
Table:- Select the
two_factor_auth
database. - Click on “New” to create a table.
- Define the table with the following fields:
- Click “Save”.
- Select the
3. Project Structure
Organize your project files as follows:
two-factor-auth/
├── vendor/ # Composer dependencies
├── assets/
│ ├── css/
│ │ └── styles.css
│ └── js/
│ └── scripts.js
├── config/
│ └── db.php
├── templates/
│ ├── header.php
│ └── footer.php
├── register.php
├── login.php
├── enable_2fa.php
├── verify_2fa.php
├── logout.php
├── README.md
└── composer.json
4. Installing Required Packages via Composer
To implement 2FA, we’ll use the PHPGangsta/GoogleAuthenticator library and PHP QR Code library for generating QR codes.
- Navigate to Project Directory:
- Open your terminal or command prompt.
- Navigate to
two-factor-auth
directory.
- Install Libraries:
composer require phpgangsta/googleauthenticator composer require endroid/qr-code
Note: Theendroid/qr-code
library is a modern QR code generator. Alternatively, you can use other QR code libraries based on preference.
5. Configuration
a. Database Connection (config/db.php
)
Create a file named db.php
inside the config
directory to handle database connections.
<?php
// config/db.php
$host = 'localhost';
$db = 'two_factor_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
http_response_code(500);
echo "Database connection failed.";
exit;
}
?>
b. Session Configuration
Ensure that session_start()
is called at the beginning of every script that uses sessions, typically in the header template.
6. 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>Two-Factor Authentication System</title>
<link rel="stylesheet" href="/two-factor-auth/assets/css/styles.css">
</head>
<body>
<header>
<h1>Two-Factor Authentication System</h1>
<nav>
<?php if (isset($_SESSION['user_id'])): ?>
<a href="/two-factor-auth/enable_2fa.php">Enable 2FA</a>
<a href="/two-factor-auth/logout.php">Logout (<?php echo htmlspecialchars($_SESSION['username']); ?>)</a>
<?php else: ?>
<a href="/two-factor-auth/register.php">Register</a>
<a href="/two-factor-auth/login.php">Login</a>
<?php endif; ?>
</nav>
</header>
<main>
b. Footer (templates/footer.php
)
<?php
// templates/footer.php
?>
</main>
<footer>
<p>© <?php echo date("Y"); ?> Your Company Name</p>
</footer>
<script src="/two-factor-auth/assets/js/scripts.js"></script>
</body>
</html>
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']);
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. It integrates 2FA if enabled.
<?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 * FROM users WHERE username = ?');
$stmt->execute([$username]);
$user = $stmt->fetch();
if ($user && password_verify($password, $user['password'])) {
// Password is correct
$_SESSION['temp_user_id'] = $user['id']; // Temporary session before 2FA
if ($user['two_fa_enabled']) {
// Redirect to 2FA verification
header('Location: verify_2fa.php');
exit;
} else {
// Start user session
$_SESSION['user_id'] = $user['id'];
$_SESSION['username'] = $user['username'];
// Redirect to dashboard or home page
header('Location: index.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. Enabling 2FA (enable_2fa.php
)
This page allows authenticated users to enable 2FA on their accounts by scanning a QR code with an authenticator app.
<?php
// enable_2fa.php
require 'config/db.php';
require 'templates/header.php';
use PHPGangsta_GoogleAuthenticator;
use Endroid\QrCode\QrCode;
use Endroid\QrCode\Writer\PngWriter;
// Check if user is logged in
if (!isset($_SESSION['user_id'])) {
header('Location: login.php');
exit;
}
$errors = [];
$success = '';
$ga = new PHPGangsta_GoogleAuthenticator();
// Fetch user information
$stmt = $pdo->prepare('SELECT two_fa_enabled, two_fa_secret FROM users WHERE id = ?');
$stmt->execute([$_SESSION['user_id']]);
$user = $stmt->fetch();
if ($user['two_fa_enabled']) {
echo "<p>Two-Factor Authentication is already enabled on your account.</p>";
require 'templates/footer.php';
exit;
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$token = trim($_POST['token']);
if (empty($token)) {
$errors[] = 'Authentication code is required.';
} else {
// Verify the token
$result = $ga->verifyCode($user['two_fa_secret'], $token, 2); // 2 = 2*30sec clock tolerance
if ($result) {
// Enable 2FA in database
$stmt = $pdo->prepare('UPDATE users SET two_fa_enabled = 1 WHERE id = ?');
if ($stmt->execute([$_SESSION['user_id']])) {
$success = 'Two-Factor Authentication has been enabled successfully.';
} else {
$errors[] = 'Failed to enable Two-Factor Authentication.';
}
} else {
$errors[] = 'Invalid authentication code. Please try again.';
}
}
}
// Generate a new secret and QR code if not already generated
if (!$user['two_fa_secret']) {
$secret = $ga->createSecret();
// Store the secret temporarily in session
$_SESSION['two_fa_secret'] = $secret;
} else {
$secret = $user['two_fa_secret'];
}
if (!isset($_SESSION['two_fa_secret'])) {
$_SESSION['two_fa_secret'] = $secret;
}
// Generate QR code
$issuer = 'YourCompanyName';
$qrCodeUrl = $ga->getQRCodeGoogleUrl($_SESSION['username'], $_SESSION['two_fa_secret'], $issuer);
// Alternatively, using Endroid QR Code for better customization
$qrCode = QrCode::create($qrCodeUrl)
->setSize(200)
->setMargin(10);
$writer = new PngWriter();
$qrCodeImage = $writer->write($qrCode)->getDataUri();
?>
<h2>Enable Two-Factor Authentication</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; ?>
<?php if (!$success): ?>
<p>Scan the QR code below with your authenticator app (e.g., Google Authenticator), then enter the generated code to enable Two-Factor Authentication.</p>
<img src="<?php echo $qrCodeImage; ?>" alt="QR Code">
<form action="enable_2fa.php" method="POST">
<div class="form-group">
<label for="token">Authentication Code:</label>
<input type="text" id="token" name="token" required>
</div>
<button type="submit">Enable 2FA</button>
</form>
<?php endif; ?>
<?php
require 'templates/footer.php';
?>
Note: Ensure that the assets/js/scripts.js
and other assets are properly set up if you plan to add interactivity or additional styles.
10. Verifying 2FA (verify_2fa.php
)
This page prompts users to enter the 2FA code after logging in if 2FA is enabled on their account.
<?php
// verify_2fa.php
require 'config/db.php';
require 'templates/header.php';
use PHPGangsta_GoogleAuthenticator;
$errors = [];
$success = '';
if (!isset($_SESSION['temp_user_id'])) {
header('Location: login.php');
exit;
}
$ga = new PHPGangsta_GoogleAuthenticator();
// Fetch user information
$stmt = $pdo->prepare('SELECT two_fa_enabled, two_fa_secret, username FROM users WHERE id = ?');
$stmt->execute([$_SESSION['temp_user_id']]);
$user = $stmt->fetch();
if (!$user || !$user['two_fa_enabled'] || !$user['two_fa_secret']) {
// 2FA not enabled or invalid user
header('Location: login.php');
exit;
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$token = trim($_POST['token']);
if (empty($token)) {
$errors[] = 'Authentication code is required.';
} else {
// Verify the token
$result = $ga->verifyCode($user['two_fa_secret'], $token, 2); // 2 = 2*30sec clock tolerance
if ($result) {
// Authentication successful, start user session
$_SESSION['user_id'] = $_SESSION['temp_user_id'];
$_SESSION['username'] = $user['username'];
unset($_SESSION['temp_user_id']);
// Redirect to home page or dashboard
header('Location: index.php');
exit;
} else {
$errors[] = 'Invalid authentication code. Please try again.';
}
}
}
?>
<h2>Two-Factor Authentication Verification</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; ?>
<form action="verify_2fa.php" method="POST">
<div class="form-group">
<label for="token">Enter Authentication Code:</label>
<input type="text" id="token" name="token" required>
</div>
<button type="submit">Verify</button>
</form>
<?php
require 'templates/footer.php';
?>
11. Logging Out Users (logout.php
)
This script logs users out by destroying their session.
<?php
// logout.php
session_start();
session_unset();
session_destroy();
// Redirect to login page
header('Location: login.php');
exit;
?>
12. Testing the Application
- Register a New User:
- Navigate to
http://localhost/two-factor-auth/register.php
. - Fill in the registration form and submit.
- Ensure that the registration is successful.
- Navigate to
- Login with the Registered User:
- Navigate to
http://localhost/two-factor-auth/login.php
. - Enter your credentials and log in.
- If 2FA is not enabled, you should be redirected to the home page.
- Navigate to
- Enable 2FA:
- While logged in, navigate to
http://localhost/two-factor-auth/enable_2fa.php
. - Scan the displayed QR code with your authenticator app (e.g., Google Authenticator).
- Enter the 2FA code generated by the app and submit.
- Ensure that 2FA is enabled successfully.
- While logged in, navigate to
- Test 2FA Verification:
- Log out by navigating to
http://localhost/two-factor-auth/logout.php
. - Log in again at
http://localhost/two-factor-auth/login.php
. - After entering your username and password, you should be redirected to
verify_2fa.php
. - Enter the current 2FA code from your authenticator app.
- Ensure that you are redirected to the home page upon successful verification.
- Log out by navigating to
- Handle Invalid 2FA Codes:
- Attempt to enter an incorrect 2FA code.
- Ensure that appropriate error messages are displayed.
- Disable 2FA (Optional Enhancement):
- Implement functionality to allow users to disable 2FA from their account settings if desired.
13. Deployment Considerations
When deploying your Two-Factor Authentication System to a live server, consider the following:
- Secure Storage of Secrets:
- Ensure that
two_fa_secret
is stored securely in the database. - Avoid exposing secrets in URLs or client-side code.
- Ensure that
- Use HTTPS:
- Implement HTTPS to secure data transmission, especially during login and 2FA verification.
- Protect Against Brute-Force Attacks:
- Implement rate limiting or account lockout mechanisms after multiple failed login or 2FA attempts.
- Session Security:
- Use secure session cookies (
session.cookie_secure
andsession.cookie_httponly
) to protect session data.
- Use secure session cookies (
- Input Validation and Sanitization:
- Thoroughly validate and sanitize all user inputs to prevent SQL injection and XSS attacks.
- Error Handling:
- Provide user-friendly error messages without exposing sensitive information.
- Regular Backups:
- Schedule regular backups of your database to prevent data loss.
- Compliance:
- Ensure that your authentication system complies with relevant security standards and regulations.
14. Enhancements and Best Practices
- Backup Codes:
- Provide users with backup codes in case they lose access to their authenticator app.
- Email-Based 2FA:
- Implement email-based 2FA as an alternative or in addition to authenticator apps.
- Recovery Mechanisms:
- Allow users to recover access if they lose their 2FA device.
- Multi-Device Support:
- Enable users to register multiple 2FA devices.
- Audit Logs:
- Maintain logs of 2FA enablement, disablement, and failed verification attempts for security auditing.
- User Notifications:
- Notify users via email or other channels when 2FA is enabled or disabled on their account.
- Mobile-Friendly Design:
- Ensure that the 2FA setup and verification pages are responsive and mobile-friendly.
- Use of Trusted Libraries:
- Rely on well-maintained and trusted libraries for 2FA implementation to ensure security and reliability.
- Educate Users:
- Provide clear instructions and support to help users set up and use 2FA effectively.
Conclusion
These next three PHP use cases—Implementing a Password Reset System, Building a Shopping Cart for an E-commerce Website, and Developing a Two-Factor Authentication (2FA) System—provide a comprehensive understanding of essential web development functionalities using PHP and MySQL. Each project is meticulously structured to guide your followers through the entire development process, from setup to deployment, ensuring they gain practical experience and confidence in building secure and functional web applications.
Next Steps for Your Followers:
- Implement the Projects:
- Follow the step-by-step guides to build each application.
- Experiment with the code to better understand how each component works.
- Enhance the Projects:
- Add new features or improve existing ones.
- Implement security best practices to safeguard applications.
- Explore PHP Frameworks:
- Once comfortable with vanilla PHP, consider exploring frameworks like Laravel, Symfony, or CodeIgniter for more advanced projects.
- Engage with the Community:
- Encourage followers to share their progress, ask questions, and collaborate on improvements.
- Stay Updated:
- PHP is continuously evolving. Stay informed about the latest PHP versions, features, and best practices.