From bc523616aebc407a141498f45e530d980c820eb1 Mon Sep 17 00:00:00 2001 From: hjlarry Date: Mon, 22 Dec 2025 17:18:24 +0800 Subject: [PATCH] fix web forgot password --- api/controllers/web/forgot_password.py | 7 ++-- .../web/test_web_forgot_password.py | 36 ++++++++++++++++++- 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/api/controllers/web/forgot_password.py b/api/controllers/web/forgot_password.py index 958ae65802..a78abbc856 100644 --- a/api/controllers/web/forgot_password.py +++ b/api/controllers/web/forgot_password.py @@ -98,7 +98,10 @@ class ForgotPasswordCheckApi(Resource): raise InvalidTokenError() token_email = token_data.get("email") - normalized_token_email = token_email.lower() if isinstance(token_email, str) else token_email + if not isinstance(token_email, str): + raise InvalidEmailError() + normalized_token_email = token_email.lower() + if user_email != normalized_token_email: raise InvalidEmailError() @@ -111,7 +114,7 @@ class ForgotPasswordCheckApi(Resource): # Refresh token data by generating a new token _, new_token = AccountService.generate_reset_password_token( - user_email, code=args["code"], additional_data={"phase": "reset"} + token_email, code=args["code"], additional_data={"phase": "reset"} ) AccountService.reset_forgot_password_error_rate_limit(user_email) diff --git a/api/tests/unit_tests/controllers/web/test_web_forgot_password.py b/api/tests/unit_tests/controllers/web/test_web_forgot_password.py index 68632b7094..b1fbd7d79d 100644 --- a/api/tests/unit_tests/controllers/web/test_web_forgot_password.py +++ b/api/tests/unit_tests/controllers/web/test_web_forgot_password.py @@ -100,12 +100,46 @@ class TestForgotPasswordCheckApi: mock_add_rate.assert_not_called() mock_revoke_token.assert_called_once_with("token-123") mock_generate_token.assert_called_once_with( - "user@example.com", + "User@Example.com", code="1234", additional_data={"phase": "reset"}, ) mock_reset_rate.assert_called_once_with("user@example.com") + @patch("controllers.web.forgot_password.AccountService.reset_forgot_password_error_rate_limit") + @patch("controllers.web.forgot_password.AccountService.generate_reset_password_token") + @patch("controllers.web.forgot_password.AccountService.revoke_reset_password_token") + @patch("controllers.web.forgot_password.AccountService.get_reset_password_data") + @patch("controllers.web.forgot_password.AccountService.is_forgot_password_error_rate_limit") + def test_should_preserve_token_email_case( + self, + mock_is_rate_limit, + mock_get_data, + mock_revoke_token, + mock_generate_token, + mock_reset_rate, + app, + ): + mock_is_rate_limit.return_value = False + mock_get_data.return_value = {"email": "MixedCase@Example.com", "code": "5678"} + mock_generate_token.return_value = (None, "fresh-token") + + with app.test_request_context( + "/web/forgot-password/validity", + method="POST", + json={"email": "mixedcase@example.com", "code": "5678", "token": "token-upper"}, + ): + response = ForgotPasswordCheckApi().post() + + assert response == {"is_valid": True, "email": "mixedcase@example.com", "token": "fresh-token"} + mock_generate_token.assert_called_once_with( + "MixedCase@Example.com", + code="5678", + additional_data={"phase": "reset"}, + ) + mock_revoke_token.assert_called_once_with("token-upper") + mock_reset_rate.assert_called_once_with("mixedcase@example.com") + class TestForgotPasswordResetApi: @patch("controllers.web.forgot_password.ForgotPasswordResetApi._update_existing_account")