DC-049 follow-on: email fallback link on TOTP-only overlay
When only TOTP is enabled (today's production state for everyone), auth-gate.js was falling through to the legacy TOTP overlay with no visible path to the email provider. The email method was unreachable from the UI even when configured. Fixed: append a small 'Or sign in with email instead ->' link to the bottom of the TOTP card. Clicking swaps the body to the email challenge form. Why this matters even for the single-totp path: email is the phone-friendly, no-app-required recovery path. Operator forgets their TOTP secret at 2am, they can request a link without touching the authenticator app. The link just wasn't reachable before. Renders the link only when the methods response includes both totp and email — preserves the truly-single-provider case unchanged.
This commit is contained in:
+42
-5
@@ -189,20 +189,57 @@
|
||||
|
||||
const providers = await fetchMethods();
|
||||
if (providers.length === 0) {
|
||||
// Either the endpoint isn't reachable OR only TOTP is enabled (which
|
||||
// isEnabled() returns false until set up). Either way, fall back to
|
||||
// the legacy TOTP overlay — existing totp-auth.js handles it.
|
||||
// Either the endpoint isn't reachable OR no provider reports enabled.
|
||||
// Fall back to the legacy TOTP overlay — existing totp-auth.js handles it.
|
||||
if (typeof window._showTotpOverlay === 'function') window._showTotpOverlay();
|
||||
return;
|
||||
}
|
||||
if (providers.length === 1 && providers[0].name === 'totp') {
|
||||
// Single TOTP provider → show the original TOTP overlay unchanged
|
||||
if (typeof window._showTotpOverlay === 'function') window._showTotpOverlay();
|
||||
// Single TOTP provider → show the original TOTP overlay unchanged,
|
||||
// but with an "Or sign in with email" link below so the email path
|
||||
// is reachable as the recovery / phone-friendly alternative. Most
|
||||
// users still want their primary method (TOTP) front-and-center.
|
||||
showTotpWithEmailFallback(providers[0]);
|
||||
return;
|
||||
}
|
||||
showProviderSelector(providers);
|
||||
}
|
||||
|
||||
function showTotpWithEmailFallback(totpProvider) {
|
||||
const emailEnabled = methodsCache && methodsCache.find(p => p.name === 'email');
|
||||
if (!emailEnabled) {
|
||||
// Truly single-provider path: legacy TOTP overlay, no alt link.
|
||||
if (typeof window._showTotpOverlay === 'function') window._showTotpOverlay();
|
||||
return;
|
||||
}
|
||||
const overlay = document.getElementById('totp-overlay');
|
||||
const card = overlay.querySelector('.totp-card');
|
||||
if (!card) return;
|
||||
|
||||
// Save the original TOTP markup so we can restore on alt-link click off.
|
||||
if (!card.dataset.originalBody) card.dataset.originalBody = card.innerHTML;
|
||||
|
||||
// Add a small "or" link at the bottom of the existing card WITHOUT
|
||||
// touching the TOTP input markup — keeps totp-auth.js's submitTotpCode
|
||||
// binding intact.
|
||||
let alt = card.querySelector('#auth-gate-email-alt');
|
||||
if (!alt) {
|
||||
const div = document.createElement('div');
|
||||
div.id = 'auth-gate-email-alt';
|
||||
div.style.cssText = 'margin-top: 18px; padding-top: 14px; border-top: 1px solid var(--border); font-size: 0.85rem;';
|
||||
div.innerHTML = `<a href="#" id="auth-gate-email-alt-link"
|
||||
style="color: var(--accent); text-decoration: none;">
|
||||
Or sign in with email instead →
|
||||
</a>`;
|
||||
card.appendChild(div);
|
||||
div.querySelector('#auth-gate-email-alt-link').addEventListener('click', e => {
|
||||
e.preventDefault();
|
||||
renderProviderChallenge(emailEnabled);
|
||||
});
|
||||
}
|
||||
overlay.classList.add('show');
|
||||
}
|
||||
|
||||
// ---- Trigger points ----
|
||||
// 1. SSO redirect from Caddy: ?auth=required
|
||||
// We claim ownership here (set window.__dc_049_handled = true)
|
||||
|
||||
Reference in New Issue
Block a user