unrhodecode/rhodecode/templates/otp_verify.mako
russell@unturf.com 722c3bd369 Prototype: OTP auth, styleguide overhaul, login/session rework
Replace password reset with email OTP verification flow.
Add auth_otp module, OTP templates, and email delivery.
Expand styleguide CSS with full component library.
Rework login, register, and admin views for cookie sessions.
Remove legacy 2FA templates and password reset flow.
Update SSH wrappers, forms, validators, and middleware.
2026-03-04 16:44:40 -05:00

97 lines
3.5 KiB
Mako

<%inherit file="base/root.mako"/>
<%def name="title()">
${_('Verify Code')}
%if c.rhodecode_name:
&middot; ${h.branding(c.rhodecode_name)}
%endif
</%def>
<div class="loginbox">
<div class="header-account">
<div id="header-inner" class="title">
<div id="logo">
<div class="logo-wrapper">
<a href="${h.route_path('home')}"><img src="${h.asset('images/rhodecode-logo-white-60x60.png')}" alt="RhodeCode"/></a>
</div>
% if c.rhodecode_name:
<div class="branding">
<a href="${h.route_path('home')}">${h.branding(c.rhodecode_name)}</a>
</div>
% endif
</div>
</div>
</div>
<rhodecode-toast id="notifications"></rhodecode-toast>
<div class="loginwrapper">
<div class="auth-image-wrapper">
<img class="sign-in-image" src="${h.asset('images/sign-in.png')}" alt="RhodeCode"/>
</div>
<div id="login">
<div class="sign-in-title">
<h1>${_('Enter verification code')}</h1>
<h4>${_('We sent a {}-digit code to {}').format(otp_digits, otp_email)}</h4>
</div>
<div class="inner form">
${h.form(request.route_path(verify_route), needs_csrf_token=False)}
<label for="otp">${_('Verification code')}:</label>
${h.text('otp', class_='focus', inputmode='numeric', autocomplete='one-time-code', maxlength=str(otp_digits), value=defaults.get('otp', ''))}
%if 'otp' in errors:
<span class="error-message">${errors.get('otp')}</span>
<br />
%endif
<p class="help-block">
${_('Check your email for the verification code. It expires in 5 minutes.')}
</p>
${h.submit('verify', _('Verify'), class_="btn sign-in")}
${h.end_form()}
<p class="links" style="margin-top: 15px;">
<button type="button" class="btn btn-link" id="resend-otp">${_('Resend code')}</button>
<span id="resend-status" style="display:none; color: var(--text-muted, #6b7280); font-size: 13px;"></span>
</p>
</div>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
var btn = document.getElementById('resend-otp');
var status = document.getElementById('resend-status');
if (btn) {
btn.addEventListener('click', function() {
btn.disabled = true;
status.style.display = 'inline';
status.textContent = 'Sending...';
var xhr = new XMLHttpRequest();
xhr.open('POST', '${request.route_path("resend_otp")}');
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.onload = function() {
var resp = JSON.parse(xhr.responseText);
status.textContent = resp.message || 'Done';
setTimeout(function() {
btn.disabled = false;
status.style.display = 'none';
}, 60000);
};
xhr.onerror = function() {
status.textContent = 'Error sending code.';
btn.disabled = false;
};
xhr.send('{}');
});
}
var otpInput = document.querySelector('input[name="otp"]');
if (otpInput) otpInput.focus();
});
</script>