Skip to content

Commit

Permalink
Password change email notification (#1791)
Browse files Browse the repository at this point in the history
* +send password changed email

* +fixes
  • Loading branch information
logerzerox authored Jan 10, 2025
1 parent a60d3b2 commit 3d853e0
Show file tree
Hide file tree
Showing 5 changed files with 124 additions and 2 deletions.
10 changes: 10 additions & 0 deletions py/core/base/providers/email.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class EmailConfig(ProviderConfig):
sendgrid_api_key: Optional[str] = None
verify_email_template_id: Optional[str] = None
reset_password_template_id: Optional[str] = None
password_changed_template_id: Optional[str] = None
frontend_url: Optional[str] = None
sender_name: Optional[str] = None

Expand Down Expand Up @@ -74,3 +75,12 @@ async def send_password_reset_email(
self, to_email: str, reset_token: str, *args, **kwargs
) -> None:
pass

@abstractmethod
async def send_password_changed_email(
self,
to_email: str,
*args,
**kwargs,
) -> None:
pass
29 changes: 27 additions & 2 deletions py/core/providers/auth/r2r_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,9 @@ async def send_verification_email(
)

await self.email_provider.send_verification_email(
user.email, verification_code, {"first_name": first_name}
to_email=user.email,
verification_code=verification_code,
dynamic_template_data={"first_name": first_name}
)

return verification_code, expiry
Expand Down Expand Up @@ -424,6 +426,14 @@ async def change_password(
id=user.id,
new_hashed_password=hashed_new_password,
)
try:
await self.email_provider.send_password_changed_email(
to_email=user.email,
dynamic_template_data={"first_name": user.name.split(" ")[0] or 'User'}
)
except Exception as e:
logger.error(f"Failed to send password change notification: {str(e)}")

return {"message": "Password changed successfully"}

async def request_password_reset(self, email: str) -> dict[str, str]:
Expand All @@ -446,7 +456,9 @@ async def request_password_reset(self, email: str) -> dict[str, str]:
user.name.split(" ")[0] if user.name else email.split("@")[0]
)
await self.email_provider.send_password_reset_email(
email, reset_token, {"first_name": first_name}
to_email=email,
reset_token=reset_token,
dynamic_template_data={"first_name": first_name}
)

return {
Expand Down Expand Up @@ -482,6 +494,19 @@ async def confirm_password_reset(
await self.database_provider.users_handler.remove_reset_token(
id=user_id
)
# Get the user information
user = await self.database_provider.users_handler.get_user_by_id(
id=user_id
)

try:
await self.email_provider.send_password_changed_email(
to_email=user.email,
dynamic_template_data={"first_name": user.name.split(" ")[0] or 'User'}
)
except Exception as e:
logger.error(f"Failed to send password change notification: {str(e)}")

return {"message": "Password reset successfully"}

async def logout(self, token: str) -> dict[str, str]:
Expand Down
16 changes: 16 additions & 0 deletions py/core/providers/email/console_mock.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,19 @@ async def send_password_reset_email(
-----------------------------
"""
)

async def send_password_changed_email(
self, to_email: str, *args, **kwargs
) -> None:
logger.info(
f"""
-------- Email Message --------
To: {to_email}
Subject: Your Password Has Been Changed
Body:
Your password has been successfully changed.
For security reasons, you will need to log in again on all your devices.
-----------------------------
"""
)
44 changes: 44 additions & 0 deletions py/core/providers/email/sendgrid.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ def __init__(self, config: EmailConfig):
config.reset_password_template_id
or os.getenv("SENDGRID_RESET_TEMPLATE_ID")
)
self.password_changed_template_id = (
config.password_changed_template_id
or os.getenv("SENDGRID_PASSWORD_CHANGED_TEMPLATE_ID")
)
self.client = SendGridAPIClient(api_key=self.api_key)
self.sender_name = config.sender_name

Expand Down Expand Up @@ -208,3 +212,43 @@ async def send_password_reset_email(
)
logger.error(error_msg)
raise RuntimeError(error_msg) from e

async def send_password_changed_email(
self,
to_email: str,
dynamic_template_data: Optional[dict] = None,
*args,
**kwargs
) -> None:
try:
if hasattr(self, 'password_changed_template_id') and self.password_changed_template_id:
await self.send_email(
to_email=to_email,
template_id=self.password_changed_template_id,
dynamic_template_data=dynamic_template_data,
)
else:
subject = "Your Password Has Been Changed"
body = """
Your password has been successfully changed.
If you did not make this change, please contact support immediately and secure your account.
"""
html_body = """
<div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto;">
<h1>Password Changed Successfully</h1>
<p>Your password has been successfully changed.</p>
</div>
"""
# Move send_email inside the else block
await self.send_email(
to_email=to_email,
subject=subject,
html_body=html_body,
body=body,
)
except Exception as e:
error_msg = f"Failed to send password change notification to {to_email}: {str(e)}"
logger.error(error_msg)
raise RuntimeError(error_msg) from e
27 changes: 27 additions & 0 deletions py/core/providers/email/smtp.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,3 +150,30 @@ async def send_password_reset_email(
body=body,
html_body=html_body,
)

async def send_password_changed_email(
self,
to_email: str,
*args,
**kwargs
) -> None:
body = """
Your password has been successfully changed.
If you did not make this change, please contact support immediately and secure your account.
"""

html_body = """
<div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto;">
<h1>Password Changed Successfully</h1>
<p>Your password has been successfully changed.</p>
</div>
"""

await self.send_email(
to_email=to_email,
subject="Your Password Has Been Changed",
body=body,
html_body=html_body,
)

0 comments on commit 3d853e0

Please sign in to comment.