How to Implement Google Authenticator 2FA Login in PHP

Passwords alone aren’t enough to keep your application secure. Attackers often crack or steal credentials, which is why Two-Factor Authentication (2FA) has become standard in modern applications.
In this tutorial, we’ll build a PHP login system with Google Authenticator that requires both a password and a one-time code from the user’s phone.
Prerequisites
- PHP 7.4+ with MySQL/MariaDB
- Composer installed
- Basic understanding of PHP sessions and authentication
Step 1: Install Google Authenticator Library
Run this in your project folder:
composer require sonata-project/google-authenticator
Step 2: Database Setup
Create a table for storing users and their 2FA secret.
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(100) NOT NULL,
password VARCHAR(255) NOT NULL,
secret VARCHAR(255) DEFAULT NULL
);
Step 3: Registration with QR Code
When a user registers, generate a secret key and QR code.
<?php
// register.php
require 'vendor/autoload.php';
use Sonata\GoogleAuthenticator\GoogleAuthenticator;
use Sonata\GoogleAuthenticator\GoogleQrUrl;
$pdo = new PDO("mysql:host=localhost;dbname=yourdb", "root", "");
// Handle registration
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$username = $_POST['username'];
$password = password_hash($_POST['password'], PASSWORD_BCRYPT);
$g = new GoogleAuthenticator();
$secret = $g->generateSecret();
$stmt = $pdo->prepare("INSERT INTO users (username, password, secret) VALUES (?, ?, ?)");
$stmt->execute([$username, $password, $secret]);
$qrCodeUrl = GoogleQrUrl::generate($username, $secret, 'MySecureApp');
}
?>
<!DOCTYPE html>
<html>
<head><title>Register</title></head>
<body>
<h2>Register</h2>
<form method="post">
Username: <input type="text" name="username" required><br>
Password: <input type="password" name="password" required><br>
<button type="submit">Register</button>
</form>
<?php if (!empty($qrCodeUrl)): ?>
<h3>Scan this QR Code in Google Authenticator</h3>
<img src="<?= $qrCodeUrl ?>">
<?php endif; ?>
</body>
</html>
👉 After registration, users must scan the QR code in the Google Authenticator app.
Step 4: Login with Password + OTP
First, verify username/password. If correct, ask for OTP.
<?php
// login.php
require 'vendor/autoload.php';
session_start();
$pdo = new PDO("mysql:host=localhost;dbname=yourdb", "root", "");
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['username'])) {
$username = $_POST['username'];
$password = $_POST['password'];
$stmt = $pdo->prepare("SELECT * FROM users WHERE username=?");
$stmt->execute([$username]);
$user = $stmt->fetch();
if ($user && password_verify($password, $user['password'])) {
$_SESSION['pending_user'] = $user;
header("Location: verify.php");
exit;
} else {
echo "❌ Invalid username or password";
}
}
?>
<!DOCTYPE html>
<html>
<head><title>Login</title></head>
<body>
<h2>Login</h2>
<form method="post">
Username: <input type="text" name="username" required><br>
Password: <input type="password" name="password" required><br>
<button type="submit">Login</button>
</form>
</body>
</html>
Step 5: OTP Verification
<?php
// verify.php
require 'vendor/autoload.php';
use Sonata\GoogleAuthenticator\GoogleAuthenticator;
session_start();
if (!isset($_SESSION['pending_user'])) {
header("Location: login.php");
exit;
}
$user = $_SESSION['pending_user'];
$g = new GoogleAuthenticator();
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$code = $_POST['otp_code'];
if ($g->checkCode($user['secret'], $code)) {
$_SESSION['authenticated'] = true;
$_SESSION['username'] = $user['username'];
unset($_SESSION['pending_user']);
header("Location: dashboard.php");
exit;
} else {
echo "❌ Invalid OTP, try again.";
}
}
?>
<!DOCTYPE html>
<html>
<head><title>Verify OTP</title></head>
<body>
<h2>Enter 6-digit code from Google Authenticator</h2>
<form method="post">
<input type="text" name="otp_code" required maxlength="6"><br>
<button type="submit">Verify</button>
</form>
</body>
</html>
Step 6: Dashboard Page (Protected)
<?php
// dashboard.php
session_start();
if (!isset($_SESSION['authenticated'])) {
header("Location: login.php");
exit;
}
?>
<!DOCTYPE html>
<html>
<head><title>Dashboard</title></head>
<body>
<h2>Welcome, <?= htmlspecialchars($_SESSION['username']) ?> 🎉</h2>
<p>You are logged in with 2FA enabled!</p>
<a href="logout.php">Logout</a>
</body>
</html>
Step 7: Logout
<?php
// logout.php
session_start();
session_destroy();
header("Location: login.php");
exit;
Security Best Practices
- Use HTTPS in production.
- Store passwords with
password_hash()
andpassword_verify()
. - Protect against brute-force by limiting login attempts.
- Provide backup codes or an option to disable 2FA via email verification in case the user loses their phone.
✅ Conclusion
We built a complete Google Authenticator 2FA login system in PHP:
- User registers → gets QR code.
- User logs in → enters username & password.
- User enters OTP from Google Authenticator.
- If correct, access is granted.
This simple setup greatly improves account security and can be integrated into any existing PHP application.