fix: make OTP option always selectable in MFA selection

page
This commit is contained in:
Crane.z
2026-03-09 14:33:47 +08:00
parent 820b831588
commit 77abd82c3a
4 changed files with 64 additions and 1 deletions

View File

@@ -12,6 +12,10 @@ class MFAOtp(BaseMFA):
display_name = MFAType.OTP.name
placeholder = _('OTP verification code')
def is_configured(self):
"""检查 OTP 是否已配置"""
return bool(self.user.otp_secret_key) if self.is_authenticated() else False
def _check_code(self, code):
from users.utils import check_otp_code
assert self.is_authenticated()

View File

@@ -15,6 +15,14 @@
<div class="form-group">
{% include '_mfa_login_field.html' %}
</div>
{% if show_otp_hint %}
<div id="otp-setup-hint" class="alert alert-info" style="display: none; margin-top: 10px; font-size: 13px;">
<i class="fa fa-info-circle"></i>
{% trans "To set up OTP, leave the field empty and click Next. You'll be guided to the setup page." %}
</div>
{% endif %}
<button id='submit_button' type="submit" class="btn btn-primary block full-width m-b">
{% trans 'Next' %}
</button>
@@ -27,4 +35,28 @@
margin-top: 15px;
}
</style>
<script>
// 当选择 OTP 时显示提示信息
{% if show_otp_hint %}
$(document).ready(function() {
const originalSelectChange = window.selectChange;
window.selectChange = function(name, onLoad) {
originalSelectChange(name, onLoad);
// 如果选择了 OTP显示提示
if (name === 'otp') {
$('#otp-setup-hint').slideDown(200);
} else {
$('#otp-setup-hint').slideUp(200);
}
};
// 页面加载时检查默认选中的 MFA 类型
const defaultMfaType = $('#mfa-select').val();
if (defaultMfaType === 'otp') {
$('#otp-setup-hint').show();
}
});
{% endif %}
</script>
{% endblock %}

View File

@@ -42,6 +42,16 @@ class UserLoginMFAView(mixins.AuthMixin, FormView):
return redirect(reverse('authentication:login-face-capture'))
elif mfa_type == MFAType.Passkey:
return redirect('/api/v1/authentication/passkeys/login/')
# 特殊处理:如果选择 OTP 且未配置,直接跳转到设置页面
if mfa_type == 'otp':
user = self.get_user_from_session()
mfa_backend = user.get_mfa_backend_by_type(mfa_type)
if mfa_backend and hasattr(mfa_backend, 'is_configured'):
if not mfa_backend.is_configured():
set_url = mfa_backend.get_enable_url()
return redirect(set_url + '?_=login_mfa')
return self.do_mfa_check(form, code, mfa_type)
def do_mfa_check(self, form, code, mfa_type):
@@ -67,7 +77,24 @@ class UserLoginMFAView(mixins.AuthMixin, FormView):
def get_context_data(self, **kwargs):
user = self.get_user_from_session()
mfa_context = self.get_user_mfa_context(user)
# 检查是否需要显示 OTP 设置提示
# 只有在有多个 MFA 选项且 OTP 未配置时才显示
mfa_backends = mfa_context.get('mfa_backends', [])
show_otp_hint = False
if len(mfa_backends) > 1: # 有多个 MFA 选项
for backend in mfa_backends:
if backend.name == 'otp':
if hasattr(backend, 'is_configured'):
show_otp_hint = not backend.is_configured()
else:
show_otp_hint = not backend.is_active()
break
kwargs.update(mfa_context)
kwargs['show_otp_hint'] = show_otp_hint
return kwargs
return kwargs

View File

@@ -6,7 +6,7 @@
>
{% for backend in mfa_backends %}
<option value="{{ backend.name }}"
{% if not backend.is_active %} disabled {% endif %}
{% if backend.name != 'otp' and not backend.is_active %} disabled {% endif %}
>
{{ backend.display_name }}
</option>