From 77abd82c3affe4631951542642f780799e106a09 Mon Sep 17 00:00:00 2001 From: "Crane.z" <1481445951@qq.com> Date: Mon, 9 Mar 2026 14:33:47 +0800 Subject: [PATCH] fix: make OTP option always selectable in MFA selection page --- apps/authentication/mfa/otp.py | 4 +++ .../templates/authentication/login_mfa.html | 32 +++++++++++++++++++ apps/authentication/views/mfa.py | 27 ++++++++++++++++ apps/templates/_mfa_login_field.html | 2 +- 4 files changed, 64 insertions(+), 1 deletion(-) diff --git a/apps/authentication/mfa/otp.py b/apps/authentication/mfa/otp.py index ac866b882..8c507282e 100644 --- a/apps/authentication/mfa/otp.py +++ b/apps/authentication/mfa/otp.py @@ -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() diff --git a/apps/authentication/templates/authentication/login_mfa.html b/apps/authentication/templates/authentication/login_mfa.html index 4e33af224..4948140a9 100644 --- a/apps/authentication/templates/authentication/login_mfa.html +++ b/apps/authentication/templates/authentication/login_mfa.html @@ -15,6 +15,14 @@
{% include '_mfa_login_field.html' %}
+ + {% if show_otp_hint %} + + {% endif %} + @@ -27,4 +35,28 @@ margin-top: 15px; } + {% endblock %} diff --git a/apps/authentication/views/mfa.py b/apps/authentication/views/mfa.py index 4898cc093..58a3b490b 100644 --- a/apps/authentication/views/mfa.py +++ b/apps/authentication/views/mfa.py @@ -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 diff --git a/apps/templates/_mfa_login_field.html b/apps/templates/_mfa_login_field.html index 7623f6532..056611080 100644 --- a/apps/templates/_mfa_login_field.html +++ b/apps/templates/_mfa_login_field.html @@ -6,7 +6,7 @@ > {% for backend in mfa_backends %}