This is an automated email from the ASF dual-hosted git repository.

asf-gitbox-commits pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/allura.git

commit 3d53e1e17b9b80be9699e3911be55c05fde9b2c4
Author: Dave Brondsema <[email protected]>
AuthorDate: Wed May 13 12:14:42 2026 -0400

    [#8607] check for email code mode when verifying email link
---
 Allura/allura/controllers/auth.py           |  5 ++++-
 Allura/allura/tests/functional/test_auth.py | 32 ++++++++++++++++++++++++++++-
 2 files changed, 35 insertions(+), 2 deletions(-)

diff --git a/Allura/allura/controllers/auth.py 
b/Allura/allura/controllers/auth.py
index a500df93d..4bc05f5b3 100644
--- a/Allura/allura/controllers/auth.py
+++ b/Allura/allura/controllers/auth.py
@@ -498,7 +498,10 @@ def login_email_verify(self, token, return_to='/', 
**kwargs):
         if not c.user.is_anonymous():
             redirect(self._verify_return_to(return_to))
 
-        if not session.get('multifactor-username'):
+        if not session.get('multifactor-username') or session.get('mode') != 
'email_code':
+            session.pop('multifactor-username', None)
+            session.pop('mode', None)
+            session.save()
             return dict(error='Your login session was disrupted.<br>Be sure to 
use the same browser when logging in and opening the email verification link.')
 
         user = M.User.by_username(session['multifactor-username'])
diff --git a/Allura/allura/tests/functional/test_auth.py 
b/Allura/allura/tests/functional/test_auth.py
index e2e568370..78027e116 100644
--- a/Allura/allura/tests/functional/test_auth.py
+++ b/Allura/allura/tests/functional/test_auth.py
@@ -44,7 +44,7 @@
 from allura.model.oauth import dummy_oauths
 from allura.lib import plugin
 from allura.lib import helpers as h
-from allura.lib.multifactor import TotpService, RecoveryCodeService
+from allura.lib.multifactor import TotpService, RecoveryCodeService, 
EmailCodeAuthenticationService
 
 
 def unentity(s):
@@ -3218,6 +3218,36 @@ def test_login_totp_untrusted_source(self, 
trusted_login_source, send_system_mai
         assert r.location == 'http://localhost/p/foo'
         assert r.session.get('username') == 'test-admin'
 
+    @mock.patch.dict(config, {'auth.email_auth_code.enabled': True})
+    def test_login_email_verify_requires_email_code_stage(self):
+        # make sure an email link can't bypass MFA
+        # not sure how it could happen to have a valid email link before MFA, 
but make sure anyway.
+
+        self._init_totp()
+
+        self.app.extra_environ = {'disable_auth_magic': 'True'}
+        user = M.User.by_username('test-admin')
+        token = EmailCodeAuthenticationService().generate_token(user)
+
+        r = self.app.get('/auth/?return_to=/p/foo')
+        f = r.forms[0]
+        encoded = self.app.antispam_field_names(f)
+        f[encoded['username']] = 'test-admin'
+        f[encoded['password']] = 'foo'
+        r = f.submit()
+
+        assert r.location.endswith('/auth/multifactor?return_to=%2Fp%2Ffoo'), r
+        r = r.follow()
+        assert r.session.get('multifactor-username') == 'test-admin'
+        assert r.session.get('mode') is None
+
+        r = 
self.app.get(f'/auth/login_email_verify?token={token}&return_to=/p/foo', 
status=200)
+
+        assert 'Your login session was disrupted' in r.text
+        assert not r.session.get('username')
+        assert not r.session.get('multifactor-username')
+        assert not r.session.get('mode')
+
     def test_login_rate_limit(self):
         self._init_totp()
 

Reply via email to