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();
|
const providers = await fetchMethods();
|
||||||
if (providers.length === 0) {
|
if (providers.length === 0) {
|
||||||
// Either the endpoint isn't reachable OR only TOTP is enabled (which
|
// Either the endpoint isn't reachable OR no provider reports enabled.
|
||||||
// isEnabled() returns false until set up). Either way, fall back to
|
// Fall back to the legacy TOTP overlay — existing totp-auth.js handles it.
|
||||||
// the legacy TOTP overlay — existing totp-auth.js handles it.
|
|
||||||
if (typeof window._showTotpOverlay === 'function') window._showTotpOverlay();
|
if (typeof window._showTotpOverlay === 'function') window._showTotpOverlay();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (providers.length === 1 && providers[0].name === 'totp') {
|
if (providers.length === 1 && providers[0].name === 'totp') {
|
||||||
// Single TOTP provider → show the original TOTP overlay unchanged
|
// Single TOTP provider → show the original TOTP overlay unchanged,
|
||||||
if (typeof window._showTotpOverlay === 'function') window._showTotpOverlay();
|
// 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;
|
return;
|
||||||
}
|
}
|
||||||
showProviderSelector(providers);
|
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 ----
|
// ---- Trigger points ----
|
||||||
// 1. SSO redirect from Caddy: ?auth=required
|
// 1. SSO redirect from Caddy: ?auth=required
|
||||||
// We claim ownership here (set window.__dc_049_handled = true)
|
// We claim ownership here (set window.__dc_049_handled = true)
|
||||||
|
|||||||
Reference in New Issue
Block a user