""" SMTP email service implementation. This module provides an SMTP-based implementation of the email service for sending authentication-related emails. """ import smtplib from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText from typing import Optional from .base import EmailService # Default email templates DEFAULT_VERIFICATION_TEMPLATE = """
Please click the link below to verify your email address:
Or copy and paste this link into your browser:
{verification_link}
This link will expire in 7 days.
If you did not request this verification, please ignore this email.
""" DEFAULT_PASSWORD_RESET_TEMPLATE = """You have requested to reset your password. Please click the link below:
Or copy and paste this link into your browser:
{reset_link}
This link will expire in 15 minutes.
If you did not request a password reset, please ignore this email.
""" class SMTPEmailService(EmailService): """ SMTP implementation of EmailService. This implementation uses standard SMTP protocol to send emails. It supports both TLS and SSL connections and allows custom HTML templates for email content. Attributes: host: SMTP server hostname. port: SMTP server port (587 for TLS, 465 for SSL). username: SMTP authentication username. password: SMTP authentication password. use_tls: Whether to use TLS (default: True). from_email: Email address to use as sender. from_name: Display name for sender (optional). base_url: Base URL for constructing verification/reset links. verification_template: HTML template for verification emails. password_reset_template: HTML template for password reset emails. """ def __init__( self, host: str, port: int, username: str, password: str, from_email: str, base_url: str, use_tls: bool = True, from_name: Optional[str] = None, verification_template: Optional[str] = None, password_reset_template: Optional[str] = None ): """ Initialize SMTP email service. Args: host: SMTP server hostname (e.g., "smtp.gmail.com"). port: SMTP server port (587 for TLS, 465 for SSL). username: SMTP authentication username. password: SMTP authentication password. from_email: Email address to use as sender. base_url: Base URL for your application (e.g., "https://myapp.com"). use_tls: Whether to use TLS encryption (default: True). from_name: Display name for sender (default: None). verification_template: Custom HTML template for verification emails (must include {verification_link} placeholder). password_reset_template: Custom HTML template for reset emails (must include {reset_link} placeholder). Example: >>> email_service = SMTPEmailService( ... host="smtp.gmail.com", ... port=587, ... username="noreply@myapp.com", ... password="app_password", ... from_email="noreply@myapp.com", ... base_url="https://myapp.com", ... from_name="My Application" ... ) """ self.host = host self.port = port self.username = username self.password = password self.use_tls = use_tls self.from_email = from_email self.from_name = from_name self.base_url = base_url.rstrip('/') # Use custom templates or defaults self.verification_template = verification_template or DEFAULT_VERIFICATION_TEMPLATE self.password_reset_template = password_reset_template or DEFAULT_PASSWORD_RESET_TEMPLATE def _send_email(self, to_email: str, subject: str, html_content: str) -> None: """ Send an email via SMTP. Internal method that handles the actual SMTP connection and sending. Args: to_email: Recipient email address. subject: Email subject line. html_content: HTML content of the email. Raises: smtplib.SMTPException: If email sending fails. """ # Create message message = MIMEMultipart("alternative") message["Subject"] = subject message["From"] = f"{self.from_name} <{self.from_email}>" if self.from_name else self.from_email message["To"] = to_email # Attach HTML content html_part = MIMEText(html_content, "html") message.attach(html_part) # Send email if self.use_tls: with smtplib.SMTP(self.host, self.port) as server: server.starttls() server.login(self.username, self.password) server.send_message(message) else: with smtplib.SMTP_SSL(self.host, self.port) as server: server.login(self.username, self.password) server.send_message(message) def send_verification_email(self, email: str, token: str) -> None: """ Send an email verification link to the user. Constructs a verification link using the base_url and token, then sends an email using the configured verification template. Args: email: The recipient's email address. token: The verification token (JWT). Raises: smtplib.SMTPException: If email sending fails. Example: >>> email_service.send_verification_email( ... "user@example.com", ... "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." ... ) """ verification_link = f"{self.base_url}/auth/verify-email?token={token}" html_content = self.verification_template.format(verification_link=verification_link) self._send_email( to_email=email, subject="Verify Your Email Address", html_content=html_content ) def send_password_reset_email(self, email: str, token: str) -> None: """ Send a password reset link to the user. Constructs a password reset link using the base_url and token, then sends an email using the configured password reset template. Args: email: The recipient's email address. token: The password reset token. Raises: smtplib.SMTPException: If email sending fails. Example: >>> email_service.send_password_reset_email( ... "user@example.com", ... "a1b2c3d4e5f6..." ... ) """ reset_link = f"{self.base_url}/reset-password?token={token}" html_content = self.password_reset_template.format(reset_link=reset_link) self._send_email( to_email=email, subject="Reset Your Password", html_content=html_content )