Redesigns credentials/2FA pages
parent
5422d63d83
commit
1b01a0a0eb
|
|
@ -23,8 +23,11 @@ $item = $_REQUEST['item'] ?? '';
|
|||
|
||||
// if a form is submitted
|
||||
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
|
||||
// Ensure security helper is available
|
||||
require_once '../app/helpers/security.php';
|
||||
$security = SecurityHelper::getInstance();
|
||||
|
||||
// Validate CSRF token
|
||||
$security->verifyCsrfToken($_POST['csrf_token'] ?? '');
|
||||
if (!$security->verifyCsrfToken($_POST['csrf_token'] ?? '')) {
|
||||
Feedback::flash('ERROR', 'DEFAULT', 'Invalid security token. Please try again.');
|
||||
header("Location: $app_root?page=credentials");
|
||||
|
|
|
|||
|
|
@ -4,80 +4,78 @@
|
|||
*/
|
||||
?>
|
||||
|
||||
<div class="container mt-4">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-8">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3>Set up two-factor authentication</h3>
|
||||
<div class="tm-cred-card mx-auto">
|
||||
<div class="tm-profile-header">
|
||||
<p class="tm-profile-eyebrow">Security</p>
|
||||
<h2 class="tm-profile-title">Set up two-factor authentication</h2>
|
||||
<p class="tm-profile-subtitle">Protect your account with an extra verification step whenever you sign in.</p>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="alert alert-info">
|
||||
<p>Two-factor authentication adds an extra layer of security to your account. Once enabled, you'll need to enter both your password and a code from your authenticator app when signing in.</p>
|
||||
|
||||
<div class="tm-cred-intro alert alert-info">
|
||||
Two-factor authentication adds an extra layer of protection. After setup, you will sign in with both your password and a code from your authenticator app.
|
||||
</div>
|
||||
|
||||
<?php if (isset($error)): ?>
|
||||
<div class="alert alert-danger">
|
||||
<div class="alert alert-danger mb-4">
|
||||
<?php echo htmlspecialchars($error); ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (isset($setupData) && is_array($setupData)): ?>
|
||||
<div class="setup-steps">
|
||||
<h4>1. Install an authenticator app</h4>
|
||||
<p>If you haven't already, install an authenticator app on your mobile device:</p>
|
||||
<ul>
|
||||
<li>Google Authenticator</li>
|
||||
<li>Microsoft Authenticator</li>
|
||||
<li>Authy</li>
|
||||
</ul>
|
||||
<div class="tm-cred-steps">
|
||||
<div class="tm-cred-step">
|
||||
<h3>1. Install an authenticator app</h3>
|
||||
<p>Use any TOTP-compatible app such as Google Authenticator, Microsoft Authenticator, or Authy.</p>
|
||||
</div>
|
||||
|
||||
<h4 class="mt-4">2. Scan the QR code</h4>
|
||||
<p>Open your authenticator app and scan this QR code:</p>
|
||||
|
||||
<div class="text-center my-4">
|
||||
<div class="tm-cred-step">
|
||||
<h3>2. Scan the QR code</h3>
|
||||
<p>Open your authenticator app and scan the QR code below.</p>
|
||||
<div class="tm-cred-qr">
|
||||
<div id="qrcode"></div>
|
||||
<div class="mt-2">
|
||||
<small class="text-muted">Can't scan? Use this code instead:</small><br>
|
||||
<code class="secret-key"><?php echo htmlspecialchars($setupData['secret']); ?></code>
|
||||
<div class="tm-cred-secret">
|
||||
<small>Can't scan? Enter this code manually:</small>
|
||||
<code><?php echo htmlspecialchars($setupData['secret']); ?></code>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h4 class="mt-4">3. Verify setup</h4>
|
||||
<p>Enter the 6-digit code from your authenticator app to verify the setup:</p>
|
||||
|
||||
<form method="post" action="?page=credentials&item=2fa&action=setup" class="mt-3">
|
||||
<div class="form-group">
|
||||
<div class="tm-cred-step">
|
||||
<h3>3. Verify setup</h3>
|
||||
<p>Enter the 6-digit code shown in your authenticator app.</p>
|
||||
<form method="post" action="?page=credentials&item=2fa&action=setup" class="tm-cred-form" novalidate>
|
||||
<div class="mb-3">
|
||||
<label for="setup_code" class="form-label">One-time code</label>
|
||||
<input type="text"
|
||||
id="setup_code"
|
||||
name="code"
|
||||
class="form-control"
|
||||
pattern="[0-9]{6}"
|
||||
maxlength="6"
|
||||
required
|
||||
placeholder="Enter 6-digit code">
|
||||
placeholder="000000">
|
||||
</div>
|
||||
|
||||
<input type="hidden" name="secret" value="<?php echo htmlspecialchars($setupData['secret']); ?>">
|
||||
<input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($_SESSION['csrf_token']); ?>">
|
||||
|
||||
<button type="submit" class="btn btn-primary mt-3">
|
||||
<button type="submit" class="btn btn-primary tm-contact-submit w-100">
|
||||
Verify and enable 2FA
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="mt-4">
|
||||
<h4>Backup codes</h4>
|
||||
<p class="text-warning">
|
||||
<strong>Important:</strong> Save these backup codes in a secure place.
|
||||
If you lose access to your authenticator app, you can use these codes to sign in.
|
||||
Each code can only be used once.
|
||||
<div class="tm-cred-step">
|
||||
<h3>Backup codes</h3>
|
||||
<p class="text-danger mb-3">
|
||||
Save these codes somewhere secure. Each code can be used once if you lose access to your authenticator app.
|
||||
</p>
|
||||
<div class="backup-codes bg-light p-3 rounded">
|
||||
<div class="tm-cred-backup">
|
||||
<?php foreach ($setupData['backupCodes'] as $code): ?>
|
||||
<code class="d-block"><?php echo htmlspecialchars($code); ?></code>
|
||||
<code><?php echo htmlspecialchars($code); ?></code>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<button class="btn btn-secondary mt-2" onclick="window.print()">
|
||||
<button class="btn btn-outline-secondary mt-3" onclick="window.print()">
|
||||
Print backup codes
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -89,10 +87,6 @@
|
|||
<a href="?page=credentials" class="btn btn-primary">Back to credentials</a>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php if (isset($setupData) && is_array($setupData)): ?>
|
||||
<script src="https://cdn.jsdelivr.net/npm/qrcodejs@1.0.0/qrcode.min.js"></script>
|
||||
|
|
|
|||
|
|
@ -4,27 +4,27 @@
|
|||
*/
|
||||
?>
|
||||
|
||||
<div class="container mt-4">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3>Two-factor authentication</h3>
|
||||
<div class="auth-card mx-auto">
|
||||
<div class="auth-card-body">
|
||||
<div class="auth-header">
|
||||
<p class="auth-eyebrow">Security check</p>
|
||||
<h2 class="auth-title">Two-factor authentication</h2>
|
||||
<p class="auth-subtitle">Enter the 6-digit code from your authenticator app to continue.</p>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
|
||||
<?php if (isset($error)): ?>
|
||||
<div class="alert alert-danger">
|
||||
<div class="alert alert-danger mb-4">
|
||||
<?php echo htmlspecialchars($error); ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<p>Enter the 6-digit code from your authenticator app:</p>
|
||||
|
||||
<form method="post" action="?page=login&action=verify" class="mt-3">
|
||||
<div class="form-group">
|
||||
<form method="post" action="?page=login&action=verify" class="auth-form" novalidate>
|
||||
<div class="text-center mb-3">
|
||||
<label for="code" class="form-label">One-time code</label>
|
||||
<input type="text"
|
||||
id="code"
|
||||
name="code"
|
||||
class="form-control form-control-lg text-center"
|
||||
class="form-control text-center auth-otp-input"
|
||||
pattern="[0-9]{6}"
|
||||
maxlength="6"
|
||||
inputmode="numeric"
|
||||
|
|
@ -36,45 +36,41 @@
|
|||
|
||||
<input type="hidden" name="user_id" value="<?php echo htmlspecialchars($userId); ?>">
|
||||
|
||||
<button type="submit" class="btn btn-primary btn-block mt-4">
|
||||
<button type="submit" class="btn btn-primary auth-submit w-100">
|
||||
Verify code
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<div class="mt-4">
|
||||
<p class="text-muted text-center">
|
||||
Lost access to your authenticator app?<br>
|
||||
<a href="#" data-toggle="collapse" data-target="#backupCodeForm">
|
||||
<div class="mt-4 text-center">
|
||||
<p class="text-muted mb-2">Lost access to your authenticator app?</p>
|
||||
<button class="btn btn-link auth-link p-0" type="button" data-toggle="collapse" data-target="#backupCodeForm">
|
||||
Use a backup code
|
||||
</a>
|
||||
</p>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="collapse mt-3" id="backupCodeForm">
|
||||
<form method="post" action="?page=login&action=verify" class="mt-3">
|
||||
<div class="form-group">
|
||||
<label>Enter backup code:</label>
|
||||
<form method="post" action="?page=login&action=verify" class="auth-form" novalidate>
|
||||
<div class="mb-3">
|
||||
<label for="backup_code" class="form-label">Backup code</label>
|
||||
<input type="text"
|
||||
id="backup_code"
|
||||
name="backup_code"
|
||||
class="form-control"
|
||||
pattern="[a-f0-9]{8}"
|
||||
maxlength="8"
|
||||
required
|
||||
placeholder="Enter backup code">
|
||||
placeholder="Enter 8-character code">
|
||||
</div>
|
||||
|
||||
<input type="hidden" name="user_id" value="<?php echo htmlspecialchars($userId); ?>">
|
||||
|
||||
<button type="submit" class="btn btn-secondary btn-block">
|
||||
<button type="submit" class="btn btn-secondary w-100">
|
||||
Use backup code
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Auto-submit when 6 digits are entered
|
||||
|
|
|
|||
|
|
@ -5,88 +5,80 @@
|
|||
*/
|
||||
?>
|
||||
|
||||
<div class="container mt-4">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-8">
|
||||
<!-- Password Management -->
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
<h3>change password</h3>
|
||||
<div class="tm-cred-card mx-auto">
|
||||
<div class="tm-profile-header">
|
||||
<p class="tm-profile-eyebrow">Security</p>
|
||||
<h2 class="tm-profile-title">Manage credentials</h2>
|
||||
<p class="tm-profile-subtitle">Update your password and keep two-factor authentication status in one place.</p>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="post" action="?page=credentials&item=password">
|
||||
|
||||
<div class="tm-cred-grid">
|
||||
<section class="tm-cred-panel">
|
||||
<div class="tm-cred-panel-head">
|
||||
<div>
|
||||
<h3>Change password</h3>
|
||||
<p>Choose a strong password to keep your account safe.</p>
|
||||
</div>
|
||||
<span class="badge bg-light text-dark">Required</span>
|
||||
</div>
|
||||
<form method="post" action="?page=credentials&item=password" class="tm-cred-form" novalidate>
|
||||
<input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($_SESSION['csrf_token']); ?>">
|
||||
|
||||
<div class="form-group">
|
||||
<label for="current_password">current password</label>
|
||||
<input type="password"
|
||||
class="form-control"
|
||||
id="current_password"
|
||||
name="current_password"
|
||||
required>
|
||||
<div class="mb-3">
|
||||
<label for="current_password" class="form-label">Current password</label>
|
||||
<input type="password" class="form-control" id="current_password" name="current_password" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group mt-3">
|
||||
<label for="new_password">new password</label>
|
||||
<input type="password"
|
||||
class="form-control"
|
||||
id="new_password"
|
||||
name="new_password"
|
||||
pattern=".{8,}"
|
||||
title="Password must be at least 8 characters long"
|
||||
required>
|
||||
<small class="form-text text-muted">minimum 8 characters</small>
|
||||
<div class="mb-3">
|
||||
<label for="new_password" class="form-label">New password</label>
|
||||
<input type="password" class="form-control" id="new_password" name="new_password" pattern=".{8,}" title="Password must be at least 8 characters long" required>
|
||||
<small class="form-text text-muted">Minimum 8 characters</small>
|
||||
</div>
|
||||
|
||||
<div class="form-group mt-3">
|
||||
<label for="confirm_password">confirm new password</label>
|
||||
<input type="password"
|
||||
class="form-control"
|
||||
id="confirm_password"
|
||||
name="confirm_password"
|
||||
pattern=".{8,}"
|
||||
required>
|
||||
<div class="mb-4">
|
||||
<label for="confirm_password" class="form-label">Confirm new password</label>
|
||||
<input type="password" class="form-control" id="confirm_password" name="confirm_password" pattern=".{8,}" required>
|
||||
</div>
|
||||
|
||||
<div class="mt-4">
|
||||
<button type="submit" class="btn btn-primary">change password</button>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary tm-contact-submit w-100">Save new password</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 2FA Management -->
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3>two-factor authentication</h3>
|
||||
<section class="tm-cred-panel">
|
||||
<div class="tm-cred-panel-head">
|
||||
<div>
|
||||
<h3>Two-factor authentication</h3>
|
||||
<p>Strengthen security with a verification code from your authenticator app.</p>
|
||||
</div>
|
||||
<span class="badge <?= $has2fa ? 'bg-success' : 'bg-warning text-dark' ?>">
|
||||
<?= $has2fa ? 'Enabled' : 'Disabled' ?>
|
||||
</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p class="mb-4">Two-factor authentication adds an extra layer of security to your account. Once enabled, you'll need to enter both your password and a code from your authenticator app when signing in.</p>
|
||||
|
||||
<?php if ($has2fa): ?>
|
||||
<div class="alert alert-success">
|
||||
<i class="fas fa-check-circle"></i> two-factor authentication is enabled
|
||||
<div class="alert alert-success d-flex align-items-center gap-2">
|
||||
<i class="fas fa-shield-check"></i>
|
||||
<span>Two-factor authentication is currently enabled.</span>
|
||||
</div>
|
||||
<form method="post" action="?page=credentials&item=2fa&action=disable">
|
||||
<form method="post" action="?page=credentials&item=2fa&action=disable" class="tm-cred-form">
|
||||
<input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($_SESSION['csrf_token']); ?>">
|
||||
<button type="submit" class="btn btn-danger" onclick="return confirm('Are you sure you want to disable two-factor authentication? This will make your account less secure.')">
|
||||
disable two-factor authentication
|
||||
<button type="submit" class="btn btn-outline-danger w-100" onclick="return confirm('Disable two-factor authentication? This will make your account less secure.')">
|
||||
Disable 2FA
|
||||
</button>
|
||||
</form>
|
||||
<?php else: ?>
|
||||
<div class="alert alert-warning">
|
||||
<i class="fas fa-exclamation-triangle"></i> two-factor authentication is not enabled
|
||||
<div class="alert alert-warning d-flex align-items-center gap-2">
|
||||
<i class="fas fa-lock"></i>
|
||||
<span>Two-factor authentication is not enabled yet.</span>
|
||||
</div>
|
||||
<form method="post" action="?page=credentials&item=2fa&action=setup">
|
||||
<form method="post" action="?page=credentials&item=2fa&action=setup" class="tm-cred-form">
|
||||
<input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($_SESSION['csrf_token']); ?>">
|
||||
<button type="submit" class="btn btn-primary">
|
||||
set up two-factor authentication
|
||||
<button type="submit" class="btn btn-outline-primary w-100">
|
||||
Set up 2FA
|
||||
</button>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -5,10 +5,114 @@ html, body {
|
|||
padding: 0;
|
||||
}
|
||||
|
||||
/* Credentials management */
|
||||
.tm-cred-card {
|
||||
max-width: 900px;
|
||||
background: rgba(255,255,255,0.97);
|
||||
border-radius: 1.5rem;
|
||||
box-shadow: 0 25px 60px rgba(15, 23, 42, 0.12);
|
||||
padding: 2.5rem;
|
||||
margin: 2.5rem auto;
|
||||
}
|
||||
|
||||
.tm-cred-intro {
|
||||
border-radius: 1rem;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.tm-cred-steps {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.tm-cred-step {
|
||||
border: 1px solid rgba(148, 163, 184, 0.2);
|
||||
border-radius: 1.2rem;
|
||||
padding: 1.5rem;
|
||||
background: rgba(248, 250, 252, 0.75);
|
||||
}
|
||||
|
||||
.tm-cred-qr {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
padding: 1.5rem;
|
||||
border-radius: 1rem;
|
||||
background: #fff;
|
||||
border: 1px dashed rgba(148, 163, 184, 0.5);
|
||||
}
|
||||
|
||||
.tm-cred-secret code,
|
||||
.tm-cred-backup code {
|
||||
display: inline-block;
|
||||
padding: 0.35rem 0.6rem;
|
||||
border-radius: 0.6rem;
|
||||
background: #0f172a;
|
||||
color: #f8fafc;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.08em;
|
||||
}
|
||||
|
||||
.tm-cred-backup {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
|
||||
gap: 0.75rem;
|
||||
padding: 1rem;
|
||||
border-radius: 1rem;
|
||||
background: #fff;
|
||||
border: 1px solid rgba(148, 163, 184, 0.2);
|
||||
}
|
||||
|
||||
.tm-cred-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.tm-cred-panel {
|
||||
border: 1px solid rgba(148, 163, 184, 0.25);
|
||||
border-radius: 1.25rem;
|
||||
padding: 1.5rem;
|
||||
background: rgba(248, 250, 252, 0.75);
|
||||
}
|
||||
|
||||
.tm-cred-panel-head {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 1rem;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.tm-cred-panel-head h3 {
|
||||
margin: 0;
|
||||
font-size: 1.1rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.tm-cred-form .form-label {
|
||||
font-size: 0.85rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.06em;
|
||||
}
|
||||
|
||||
.tm-cred-form .form-control {
|
||||
border-radius: 0.85rem;
|
||||
border: 1px solid rgba(148, 163, 184, 0.7);
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.tm-cred-card {
|
||||
padding: 2rem 1.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* Profile form */
|
||||
.tm-profile-card {
|
||||
max-width: 1100px;
|
||||
margin: 2.5rem auto;
|
||||
margin: 0rem auto 1.5rem;
|
||||
background: rgba(255, 255, 255, 0.97);
|
||||
border-radius: 1.5rem;
|
||||
box-shadow: 0 30px 70px rgba(15, 23, 42, 0.12);
|
||||
|
|
|
|||
Loading…
Reference in New Issue