|
Server : LiteSpeed System : Linux terra.hostitbro.com 5.14.0-611.54.3.el9_7.x86_64 #1 SMP PREEMPT_DYNAMIC Thu May 7 16:31:24 EDT 2026 x86_64 User : outerorb ( 1091) PHP Version : 8.1.34 Disable Function : mail Directory : /home2/outerorb/emp.outerorbittech.in/ | |
|
Path: /home2/outerorb/emp.outerorbittech.in/2fa-verify.php
Size: 11.44 KB
Permissions: 0666
<?php
require __DIR__ . '/includes/helpers.php';
// Check if user is in 2FA verification state
if (!isset($_SESSION['user_id_temp']) || !isset($_SESSION['role_temp'])) {
redirect_with_message('index.php', 'Please login first.', 'error');
}
$flash = flash();
$error = '';
$remaining_attempts = 3;
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// CSRF check
if (!isset($_POST['csrf_token']) || !verify_csrf($_POST['csrf_token'])) {
$error = 'Invalid session token. Please try again.';
} else {
$token_type = sanitize_text($_POST['token_type'] ?? 'totp');
$token = sanitize_text($_POST['token'] ?? '');
$user_id = $_SESSION['user_id_temp'];
if ($token_type === 'totp') {
// Verify TOTP token
if (verify_2fa_token($user_id, $token)) {
// Success! Create session
clear_rate_limit('2fa_' . $user_id);
regenerate_session_id();
$redirectRole = $_SESSION['role_temp'];
$pendingData = $_SESSION['pending_session_data'] ?? null;
if ($redirectRole === 'employee') {
$_SESSION['employee_logged_in'] = true;
$_SESSION['employee'] = $pendingData;
} elseif ($redirectRole === 'admin') {
$_SESSION['admin_logged_in'] = true;
$_SESSION['admin'] = $pendingData;
}
// Clear temporary session vars
unset($_SESSION['user_id_temp'], $_SESSION['role_temp'], $_SESSION['pending_session_data']);
$dashboard = $redirectRole === 'admin' ? 'admin/dashboard.php' : 'employee/dashboard.php';
header('Location: ' . $dashboard);
exit;
} else {
// Rate limit 2FA attempts
$rateLimitKey = '2fa_' . $user_id;
check_rate_limit($rateLimitKey, 3, 300);
$error = 'Invalid authenticator code. Please try again.';
}
} elseif ($token_type === 'backup') {
// Verify backup code
$pdo = db();
try {
$stmt = $pdo->prepare("
SELECT id FROM two_factor_backups
WHERE user_id = ? AND used_at IS NULL
AND backup_code = ?
LIMIT 1
");
$stmt->execute([$user_id, $token]);
$backup = $stmt->fetch();
if ($backup) {
// Mark backup code as used
$pdo->prepare("UPDATE two_factor_backups SET used_at = NOW() WHERE id = ?")->execute([$backup['id']]);
// Create session
clear_rate_limit('2fa_' . $user_id);
regenerate_session_id();
$redirectRole = $_SESSION['role_temp'];
$pendingData = $_SESSION['pending_session_data'] ?? null;
if ($redirectRole === 'employee') {
$_SESSION['employee_logged_in'] = true;
$_SESSION['employee'] = $pendingData;
} elseif ($redirectRole === 'admin') {
$_SESSION['admin_logged_in'] = true;
$_SESSION['admin'] = $pendingData;
}
unset($_SESSION['user_id_temp'], $_SESSION['role_temp'], $_SESSION['pending_session_data']);
$dashboard = $redirectRole === 'admin' ? 'admin/dashboard.php' : 'employee/dashboard.php';
header('Location: ' . $dashboard);
exit;
} else {
$error = 'Invalid backup code.';
}
} catch (Exception $e) {
$error = 'Error verifying backup code.';
}
} else {
$error = 'Invalid verification method.';
}
}
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Two-Factor Authentication - Outer Orbit HRMS</title>
<link rel="stylesheet" href="assets/css/style.css?v=<?php echo filemtime(__DIR__ . '/assets/css/style.css'); ?>" />
<link rel="stylesheet" href="assets/css/polish.css?v=<?php echo filemtime(__DIR__ . '/assets/css/polish.css'); ?>" />
<style>
.auth-wrap { max-width: 440px; margin: 60px auto 0; }
.auth-logo { text-align: center; margin-bottom: 28px; }
.auth-logo h1 { font-size: 1.5rem; margin: 0 0 4px; color: #111827; }
.auth-logo p { margin: 0; color: #6b7280; font-size: .875rem; }
.auth-tabs { display: flex; gap: 0; margin-bottom: 24px; border: 1px solid #e5e7eb; border-radius: 8px; overflow: hidden; }
.auth-tab { flex: 1; padding: 10px; text-align: center; cursor: pointer; background: #f9fafb; border: none; font-size: .9rem; font-weight: 600; color: #6b7280; transition: background .15s, color .15s; }
.auth-tab.active { background: #2563eb; color: #fff; }
.input-label { display: block; font-size: .85rem; font-weight: 600; color: #374151; margin-bottom: 5px; }
.tab-content { display: none; }
.tab-content.active { display: block; }
.backup-info { background: #f0f9ff; border-left: 4px solid #2563eb; padding: 12px; margin: 16px 0; border-radius: 4px; font-size: .875rem; color: #1e40af; }
.text-center { text-align: center; }
.text-sm { font-size: .875rem; }
</style>
</head>
<body style="background: #f3f4f6; min-height: 100vh;">
<div class="auth-wrap">
<div class="auth-logo">
<h1>Outer Orbit Technologies</h1>
<p>Two-Factor Authentication</p>
</div>
<div class="card">
<?php if ($flash): ?>
<div class="alert <?php echo $flash['type'] === 'error' ? 'alert-error' : 'alert-success'; ?>">
<?php echo htmlspecialchars($flash['message'], ENT_QUOTES, 'UTF-8'); ?>
</div>
<?php endif; ?>
<?php if ($error): ?>
<div class="alert alert-error"><?php echo htmlspecialchars($error, ENT_QUOTES, 'UTF-8'); ?></div>
<?php endif; ?>
<p style="text-align: center; color: #6b7280; margin-bottom: 24px;" class="text-sm">
Enter the 6-digit code from your authenticator app
</p>
<!-- Auth method tabs -->
<div class="auth-tabs">
<button type="button" class="auth-tab active" data-tab="totp">Authenticator</button>
<button type="button" class="auth-tab" data-tab="backup">Backup Code</button>
</div>
<form method="post" action="2fa-verify.php" novalidate>
<input type="hidden" name="csrf_token" value="<?php echo csrf_token(); ?>">
<input type="hidden" name="token_type" id="token-type" value="totp">
<!-- TOTP Tab -->
<div id="totp-tab" class="tab-content active">
<div style="display: flex; flex-direction: column; gap: 16px;">
<div>
<label class="input-label" for="totp">Authenticator Code</label>
<input
id="totp"
type="text"
inputmode="numeric"
pattern="[0-9]{6}"
maxlength="6"
required
autocomplete="off"
placeholder="000000"
style="width: 100%; box-sizing: border-box; font-size: 1.5rem; letter-spacing: 8px; text-align: center; font-weight: bold;"
/>
<p style="margin: 8px 0 0 0; font-size: .8rem; color: #6b7280;">
Enter the 6-digit code from your authenticator app (Google Authenticator, Microsoft Authenticator, Authy, etc.)
</p>
</div>
<button type="submit" style="width: 100%;" class="btn btn-primary">Verify Code</button>
</div>
</div>
<!-- Backup Code Tab -->
<div id="backup-tab" class="tab-content">
<div style="display: flex; flex-direction: column; gap: 16px;">
<div class="backup-info">
💡 Use this if you don't have access to your authenticator app. Each backup code can be used only once.
</div>
<div>
<label class="input-label" for="backup">Backup Code</label>
<input
id="backup"
type="text"
required
autocomplete="off"
placeholder="Enter your backup code"
style="width: 100%; box-sizing: border-box;"
/>
<p style="margin: 8px 0 0 0; font-size: .8rem; color: #6b7280;">
You saved these backup codes when you first enabled two-factor authentication.
</p>
</div>
<button type="submit" style="width: 100%;" class="btn btn-primary">Verify Backup Code</button>
</div>
</div>
</form>
<hr style="margin: 24px 0; border: none; border-top: 1px solid #e5e7eb;">
<p style="text-align: center; font-size: .85rem; color: #6b7280;">
Having trouble? <a href="index.php" style="color: #2563eb; text-decoration: none;">Start over</a>
</p>
</div>
</div>
<script>
(function() {
const tabs = document.querySelectorAll('.auth-tab');
const totpInput = document.getElementById('totp');
const backupInput = document.getElementById('backup');
const tokenTypeInput = document.getElementById('token-type');
tabs.forEach(tab => {
tab.addEventListener('click', function() {
const tabName = this.getAttribute('data-tab');
// Update active tab
tabs.forEach(t => t.classList.remove('active'));
this.classList.add('active');
// Update active content
document.querySelectorAll('.tab-content').forEach(content => {
content.classList.remove('active');
});
document.getElementById(tabName + '-tab').classList.add('active');
// Update hidden token type
tokenTypeInput.value = tabName;
// Clear and focus appropriate input
if (tabName === 'totp') {
backupInput.value = '';
totpInput.focus();
} else {
totpInput.value = '';
backupInput.focus();
}
});
});
// Auto-submit when 6 digits entered in TOTP
totpInput.addEventListener('input', function() {
this.value = this.value.replace(/[^0-9]/g, '').slice(0, 6);
if (this.value.length === 6) {
document.querySelector('form').submit();
}
});
// Focus TOTP input on load
totpInput.focus();
})();
</script>
</body>
</html>