1
0
mirror of git://f0xx.org/ac/ac-be-auth synced 2026-07-29 05:58:21 +03:00

feat(2fa): cross-device approval UI, polling JS, approve view

- two_factor_challenge.php: QR now shown above the code field with
  data-poll-token/url attrs; approval status banner; updated caption
- two_factor_approve.php: new mobile confirmation page (Approve/Cancel)
- two_factor.js: adds poll loop every 2.5s; auto-redirect on approval;
  expires QR visually; stops polling on manual TOTP submit

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Anton Afanasyeu
2026-06-27 22:49:50 +02:00
parent 393c0c1f29
commit d8d15d77ad
3 changed files with 183 additions and 10 deletions

View File

@@ -19,7 +19,12 @@ $twofaQr = AuthTwoFactorPage::qrPayload();
</script>
<link rel="stylesheet" href="<?= h($bp) ?>/assets/css/app.css">
<script src="<?= h($bp) ?>/assets/js/i18n.js" defer></script>
<script src="<?= h($bp) ?>/assets/js/two_factor.js" defer></script>
<script src="<?= h($bp) ?>/assets/js/two_factor.js" defer
<?php if (is_array($twofaQr) && !empty($twofaQr['raw_token'])): ?>
data-poll-token="<?= h($twofaQr['raw_token']) ?>"
data-poll-url="<?= h($auth) ?>/api/two-factor/poll"
<?php endif; ?>
></script>
</head>
<body class="login-page" data-base-path="<?= h($bp) ?>">
<div class="login-locale">
@@ -33,16 +38,26 @@ $twofaQr = AuthTwoFactorPage::qrPayload();
</div>
<form class="login-card" method="post" action="<?= h($auth) ?>/two-factor" id="twofa-form">
<h1 data-i18n="twofa.title">Two-factor authentication</h1>
<p class="muted" data-i18n="twofa.hint">Enter the 6-digit code from your authenticator app.</p>
<?php if (is_array($twofaQr) && !empty($twofaQr['qr_url'])): ?>
<p class="muted" data-i18n="twofa.hint_scan">Scan the QR code with your phone to approve this sign-in, or enter the 6-digit code below.</p>
<figure class="twofa-qr-wrap" id="twofa-qr-wrap">
<img src="<?= h((string) $twofaQr['qr_url']) ?>" width="160" height="160" alt="QR code — scan to approve sign-in on this device" class="twofa-qr">
<figcaption class="muted">
Scan to approve on mobile
<?php if (!empty($twofaQr['short_url'])): ?>
· <a href="<?= h((string) $twofaQr['short_url']) ?>" rel="noopener"><?= h((string) $twofaQr['short_url']) ?></a>
<?php endif; ?>
</figcaption>
</figure>
<div id="twofa-approval-status" class="muted" style="margin-bottom:0.75rem;display:none;">
⏳ Waiting for mobile approval…
</div>
<?php else: ?>
<p class="muted" data-i18n="twofa.hint">Enter the 6-digit code from your authenticator app.</p>
<?php endif; ?>
<?php if (!empty($twofaError)): ?>
<div class="alert" data-i18n="twofa.error"><?= h($twofaError) ?></div>
<?php endif; ?>
<?php if (is_array($twofaQr) && !empty($twofaQr['qr_url'])): ?>
<figure class="twofa-qr-wrap">
<img src="<?= h((string) $twofaQr['qr_url']) ?>" width="160" height="160" alt="QR code for mobile sign-in link" class="twofa-qr">
<figcaption class="muted">Scan to open the hub on your phone<?php if (!empty($twofaQr['short_url'])): ?> · <a href="<?= h((string) $twofaQr['short_url']) ?>" rel="noopener"><?= h((string) $twofaQr['short_url']) ?></a><?php endif; ?></figcaption>
</figure>
<?php endif; ?>
<label><span data-i18n="twofa.code">Authentication code</span>
<input id="twofa-code-input" name="code" inputmode="numeric" pattern="[0-9]{6}" maxlength="6" autocomplete="one-time-code" required autofocus></label>
<button type="submit" data-i18n="twofa.submit">Continue</button>