v1.0.0.1
StocketBase
- <?php
-
- declare(strict_types=1);
-
- if (count(get_included_files()) === 1) {
- http_response_code(403);
- exit;
- }
-
- $twoFactorEnabled = TwoFactor::isEnabled($currentUser);
- $setupSecret = $twoFactorEnabled ? '' : (string) ($_SESSION['totp_setup_secret'] ?? '');
- $newCodes = $_SESSION['totp_new_codes'] ?? null;
- unset($_SESSION['totp_new_codes']);
- $issuer = (string) Config::get('APP_NAME', 'StocketBase');
-
- ?>
- <h1 class="dash-title"><?= Icons::icon('lock', 'icon icon-lg') ?>Security</h1>
-
- <?php if (is_array($newCodes) && !empty($newCodes)): ?>
- <div class="sidebar-box" style="max-width:640px;margin-bottom:20px;">
- <h2 class="dash-subtitle">Backup codes</h2>
- <p>Keep these somewhere safe. If you lose your phone, each code gets you in once. You won't see them again.</p>
- <pre style="font-size:16px;line-height:1.8;"><?= htmlspecialchars(implode("\n", $newCodes)) ?></pre>
- </div>
- <?php endif; ?>
-
- <div class="sidebar-box" style="max-width:640px;">
- <h2 class="dash-subtitle">Two-step login</h2>
-
- <?php if ($twoFactorEnabled): ?>
- <p>Status: <span class="status-pill status-published">On</span> · backup codes left: <?= TwoFactor::remainingRecoveryCodes($currentUser) ?></p>
-
- <form method="post" style="margin-top:16px;">
- <?= Csrf::field() ?>
- <input type="hidden" name="action" value="totp_regenerate">
- <label>Password</label>
- <input type="password" name="password" required autocomplete="current-password">
- <label>Code from your app, or a backup code</label>
- <input type="text" name="code" required autocomplete="one-time-code" maxlength="16" spellcheck="false">
- <button type="submit" class="dash-btn" style="margin-top:12px;"><?= Icons::icon('refresh', 'icon icon-sm') ?>Get new backup codes</button>
- </form>
-
- <form method="post" style="margin-top:24px;" onsubmit="return confirm('Turn off two-step login? Your account will only be protected by your password.');">
- <?= Csrf::field() ?>
- <input type="hidden" name="action" value="totp_disable">
- <label>Password</label>
- <input type="password" name="password" required autocomplete="current-password">
- <label>Code from your app, or a backup code</label>
- <input type="text" name="code" required autocomplete="one-time-code" maxlength="16" spellcheck="false">
- <button type="submit" class="dash-btn dash-btn-danger" style="margin-top:12px;"><?= Icons::icon('x', 'icon icon-sm') ?>Turn off two-step login</button>
- </form>
-
- <?php elseif ($setupSecret !== ''): ?>
- <p>1. Open an authenticator app (Google Authenticator, Authy, 1Password, Aegis and so on) and scan this QR code.</p>
- <div id="totp-qr" data-uri="<?= htmlspecialchars(Totp::uri($setupSecret, (string) $currentUser['username'], $issuer), ENT_QUOTES) ?>" style="width:220px;max-width:100%;margin:12px 0;border-radius:8px;overflow:hidden;background:#fff;"></div>
- <p style="font-size:13px;color:var(--muted);">Can't scan it? Type this key into the app instead:</p>
- <p style="font-size:20px;letter-spacing:.08em;margin:8px 0;"><code><?= htmlspecialchars(Totp::formatSecret($setupSecret)) ?></code></p>
- <p style="font-size:12px;color:var(--muted);word-break:break-all;">Account: <?= htmlspecialchars((string) $currentUser['username']) ?> · Store: <?= htmlspecialchars($issuer) ?></p>
- <?= Asset::js('assets/vendor/qrcode.js') ?>
- <script>
- (function () {
- var box = document.getElementById('totp-qr');
- if (!box || typeof qrcode !== 'function') { return; }
-
- var code = qrcode(0, 'M');
- code.addData(box.getAttribute('data-uri'));
- code.make();
- box.innerHTML = code.createSvgTag({ cellSize: 4, margin: 16, scalable: true });
- })();
- </script>
- <p style="margin-top:14px;">2. Type the 6-digit code from the app to finish.</p>
-
- <form method="post">
- <?= Csrf::field() ?>
- <input type="hidden" name="action" value="totp_confirm">
- <label>6-digit code</label>
- <input type="text" name="code" required inputmode="numeric" autocomplete="one-time-code" maxlength="8" pattern="[0-9 ]*" spellcheck="false">
- <div class="publish-actions">
- <button type="submit" class="dash-btn dash-btn-primary"><?= Icons::icon('check', 'icon icon-sm') ?>Turn on</button>
- </div>
- </form>
-
- <form method="post" style="margin-top:8px;">
- <?= Csrf::field() ?>
- <input type="hidden" name="action" value="totp_cancel">
- <button type="submit" class="dash-btn"><?= Icons::icon('x', 'icon icon-sm') ?>Cancel</button>
- </form>
-
- <?php else: ?>
- <p>Status: <span class="status-pill status-draft">Off</span></p>
- <p style="margin:10px 0 14px;">Make your account harder to break into. After your password, you'll also type a 6-digit code from your phone.</p>
- <form method="post">
- <?= Csrf::field() ?>
- <input type="hidden" name="action" value="totp_begin">
- <button type="submit" class="dash-btn dash-btn-primary"><?= Icons::icon('lock', 'icon icon-sm') ?>Set up</button>
- </form>
- <?php endif; ?>
- </div>
-