diff --git a/api/controllers/console/auth/error.py b/api/controllers/console/auth/error.py index 4c102bda58..4658d2c1e9 100644 --- a/api/controllers/console/auth/error.py +++ b/api/controllers/console/auth/error.py @@ -49,3 +49,9 @@ class EmailPasswordLoginLimitError(BaseHTTPException): "Too many incorrect password attempts. Please verify your identity with the email code to complete login." ) code = 429 + + +class EmailCodeLoginRateLimitExceededError(BaseHTTPException): + error_code = "email_code_login_rate_limit_exceeded" + description = "Too many login emails have been sent. Please try again in 5 minutes." + code = 429 diff --git a/api/controllers/console/auth/forgot_password.py b/api/controllers/console/auth/forgot_password.py index 7b63b36e7d..04b911d5e5 100644 --- a/api/controllers/console/auth/forgot_password.py +++ b/api/controllers/console/auth/forgot_password.py @@ -43,14 +43,10 @@ class ForgotPasswordSendEmailApi(Resource): ) else: raise NotAllowedRegister() - elif account: - try: - token = AccountService.send_reset_password_email( - account=account, email=args["email"], language=args["language"] or "en-US" - ) - except RateLimitExceededError: - logging.warning(f"Rate limit exceeded for email: {args['email']}") - raise PasswordResetRateLimitExceededError() + else: + token = AccountService.send_reset_password_email( + account=account, email=args["email"], language=args["language"] or "en-US" + ) return {"result": "success", "data": token} diff --git a/api/services/account_service.py b/api/services/account_service.py index cf4d65a27d..65d9959818 100644 --- a/api/services/account_service.py +++ b/api/services/account_service.py @@ -12,7 +12,6 @@ from werkzeug.exceptions import Unauthorized from configs import dify_config from constants.languages import language_timezone_mapping, languages -from controllers.console.auth.error import EmailPasswordLoginLimitError from events.tenant_event import tenant_was_created from extensions.ext_redis import redis_client from libs.helper import RateLimiter, TokenManager @@ -34,7 +33,6 @@ from services.errors.account import ( LinkAccountIntegrateError, MemberNotInTenantError, NoPermissionError, - RateLimitExceededError, RoleAlreadyAssignedError, TenantNotFoundError, ) @@ -45,7 +43,7 @@ from tasks.mail_reset_password_task import send_reset_password_mail_task class AccountService: - reset_password_rate_limiter = RateLimiter(prefix="reset_password_rate_limit", max_attempts=5, time_window=60 * 60) + reset_password_rate_limiter = RateLimiter(prefix="reset_password_rate_limit", max_attempts=1, time_window=60 * 1) email_code_login_rate_limiter = RateLimiter( prefix="email_code_login_rate_limit", max_attempts=1, time_window=60 * 1 ) @@ -263,7 +261,9 @@ class AccountService: account_language = account.interface_language if account else language if cls.reset_password_rate_limiter.is_rate_limited(account_email): - raise RateLimitExceededError(f"Rate limit exceeded for email: {account_email}. Please try again later.") + from controllers.console.auth.error import PasswordResetRateLimitExceededError + + raise PasswordResetRateLimitExceededError() code = "".join([str(random.randint(0, 9)) for _ in range(6)]) token = TokenManager.generate_token( @@ -290,7 +290,9 @@ class AccountService: cls, account: Optional[Account] = None, email: Optional[str] = None, language: Optional[str] = "en-US" ): if cls.email_code_login_rate_limiter.is_rate_limited(email): - raise EmailPasswordLoginLimitError() + from controllers.console.auth.error import EmailCodeLoginRateLimitExceededError + + raise EmailCodeLoginRateLimitExceededError() code = "".join([str(random.randint(0, 9)) for _ in range(6)]) token = TokenManager.generate_token(