I am trying to get authentication working using aiosmtpd... I have read the documentation but still can't get it working.
I have followed the instructions on this post: python aiosmtpd server with basic authentication , but still it isn't work
I have the following as the aiosmtpd server:
import logging
from aiosmtpd.controller import Controller
from aiosmtpd.smtp import AuthResult, LoginPassword
auth_db = {
b"user1": b"password1",
b"user2": b"password2",
b"user3": b"password3",
}
# Authenticator function
def authenticator_func(server, session, envelope, mechanism, auth_data):
# For this simple example, we'll ignore other parameters
assert isinstance(auth_data, LoginPassword)
username = auth_data.login
password = auth_data.password
# I am returning always AuthResult(success=True) just for testing
if auth_db.get(username) == password:
return AuthResult(success=True)
else:
return AuthResult(success=True)
# return AuthResult(success=False, handled=False)
class CustomSMTPHandler:
async def handle_DATA(self, server, session, envelope):
print('End of message')
return '250 Message accepted for delivery'
logging.basicConfig(level=logging.DEBUG)
handler = CustomSMTPHandler()
controller = Controller(
handler,
hostname='192.168.1.1',
port=8025,
authenticator=authenticator_func, # i.e., the name of your authenticator function
auth_required=True, # Depending on your needs
)
controller.start()
input("Servidor iniciado. Presione Return para salir.")
controller.stop()
And this is the client:
import time
import yaml
import smtplib
import os
from smtplib import SMTPException
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
# Variables ambiente
SMTP_SERVER = "192.168.1.1"
SMTP_PORT = 8025
def send_mail(mail_from, mail_to, mail_subject, mail_message):
try:
# Crea conexion a servidor
smtp_server = smtplib.SMTP(SMTP_SERVER, SMTP_PORT)
smtp_server.set_debuglevel(True)
# Creacion de mensage
msg = MIMEMultipart()
msg['From'] = mail_from
msg['To'] = mail_to
msg['Subject'] = mail_subject
msg.attach(MIMEText(mail_message, 'html'))
smtp_server.login('user1', 'password1')
# Envio de correo
smtp_server.send_message(msg)
# Cierra conexion
smtp_server.quit()
# print("Email sent successfully!")
except SMTPException as smtp_err:
return {"Error": f"Error SMTP: {repr(smtp_err)}"}
# print("SMTP Exception...", smtp_err)
except Exception as gen_err:
return {"Error": f"Error general: {repr(gen_err)}"}
# print("Something went wrong….", gen_err)
return {"Exito": f"Correo enviado a servidor SMTP: {SMTP_SERVER}"}
def main():
st = time.time()
for _ in range(1):
resp = send_mail("test@sss.com",
"ch@test.com",
"Subject del correo",
"<b>Hola</b>")
print(resp)
et = time.time()
elapsed_time = et - st
print('Execution time:', elapsed_time, 'seconds')
if __name__ == "__main__":
main()
I am getting the following as the server output:
INFO:mail.log:Available AUTH mechanisms: LOGIN(builtin) PLAIN(builtin)
INFO:mail.log:Peer: ('192.168.1.X', 54186)
INFO:mail.log:('192.168.1.X', 54186) handling connection
DEBUG:mail.log:('192.168.1.X', 54186) << b'220 PC-Name Python SMTP 1.4.5'
DEBUG:mail.log:_handle_client readline: b'EHLO [192.168.1.X]\r\n'
INFO:mail.log:('192.168.1.X', 54186) >> b'EHLO [192.168.1.X]'
DEBUG:mail.log:('192.168.1.X', 54186) << b'250-PC-Name'
DEBUG:mail.log:('192.168.1.X', 54186) << b'250-SIZE 33554432'
DEBUG:mail.log:('192.168.1.X', 54186) << b'250-8BITMIME'
DEBUG:mail.log:('192.168.1.X', 54186) << b'250-SMTPUTF8'
DEBUG:mail.log:('192.168.1.X', 54186) << b'250 HELP'
DEBUG:mail.log:_handle_client readline: b'MAIL FROM:<test@sss.com> BODY=8BITMIME SIZE=890\r\n'
INFO:mail.log:('192.168.1.X', 54186) >> b'MAIL FROM:<ch@test.com> BODY=8BITMIME SIZE=890'
INFO:mail.log:MAIL: Authentication required
DEBUG:mail.log:('192.168.1.X', 54186) << b'530 5.7.0 Authentication required'
INFO:mail.log:('192.168.1.X', 54186) EOF received
INFO:mail.log:('192.168.1.X', 54186) Connection lost during _handle_client()
INFO:mail.log:('192.168.1.X', 54186) connection lost
I am also not able to send emails using thunderbird.
Any ideas?
Posting client log after adding the line:
smtp_server.set_debuglevel(True)
to the send_mail function.
Log:
send: 'ehlo [192.168.1.X]\r\n'
reply: b'250-PC-Name-7010\r\n'
reply: b'250-SIZE 33554432\r\n'
reply: b'250-8BITMIME\r\n'
reply: b'250-SMTPUTF8\r\n'
reply: b'250 HELP\r\n'
reply: retcode (250); Msg: b'PC-Name\nSIZE 33554432\n8BITMIME\nSMTPUTF8\nHELP'
{'Error': "Error SMTP: SMTPNotSupportedError('SMTP AUTH extension not supported by server.')"}
Well, I've got it working... I changed code on both server (following advice from @tripleee about Authenticator instance) to handle authentication and on the client to send the "LOGIN" and/or "PLAIN" authentication method to the server (those are the methods aiosmtpd can handle by default).
Here is the server code:
import logging
import aiosmtpd.controller
from aiosmtpd.smtp import Envelope, AuthResult, LoginPassword
from email import message_from_bytes
from email.message import Message
from email.policy import default
class Authenticator:
def __call__(self, server, session, envelope, mechanism, auth_data):
test_passwd = "1234"
username = auth_data.login
password = auth_data.password
print(f'Username: {username}')
print(f'Password: {password}')
print(f'Password_test: {testpasswd}')
if password == test_passwd:
resp = AuthResult(success=True)
else:
resp = AuthResult(success=False, handled=False)
return resp
class CustomSMTPHandler:
async def handle_DATA(self, server, session, envelope):
""" Used to handle other stuff"""
print('End of message')
return '250 Message accepted for delivery'
def main():
handler = CustomSMTPHandler()
auth = Authenticator()
server = aiosmtpd.controller.Controller(handler,
hostname='192.168.1.1',
port=8025,
authenticator=auth,
auth_required=True,
auth_require_tls=False
)
server.start()
input("Servidor iniciado. Presione Return para salir.")
server.stop()
if __name__ == '__main__':
logging.basicConfig(level=logging.DEBUG)
main()
Note: By defaul "auth_require_tls" is set to True for security reasons (as it should be!)... I changed parameter for testing.
Here it is the client code:
import time
import smtplib
import os
from smtplib import SMTPException
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
SMTP_SERVER = "192.168.1.1"
SMTP_PORT = 8025
def send_mail(mail_from, mail_to, mail_subject, mail_message):
try:
smtp_server = smtplib.SMTP(SMTP_SERVER, SMTP_PORT)
smtp_server.set_debuglevel(True)
msg = MIMEMultipart()
msg['From'] = mail_from
msg['To'] = mail_to
msg['Subject'] = mail_subject
msg.attach(MIMEText(mail_message, 'html'))
smtp_server.esmtp_features['auth'] = 'PLAIN'
smtp_server.login('user1', '1234')
smtp_server.send_message(msg)
smtp_server.quit()
except SMTPException as smtp_err:
return {"Error": f"Error SMTP: {repr(smtp_err)}"}
except Exception as gen_err:
return {"Error": f"Error general: {repr(gen_err)}"}
return {"Exito": f"Correo enviado a servidor SMTP: {SMTP_SERVER}"}
def main():
st = time.time()
for _ in range(1):
resp = send_mail("test@sss.com",
"ch@test.com",
"Subject del correo",
"<b>Hola</b>")
print(resp)
et = time.time()
elapsed_time = et - st
print('Execution time:', elapsed_time, 'seconds')
if __name__ == "__main__":
main()
The change on the client is the "smtp_server.esmtp_features['auth'] = 'PLAIN'" line who tells the authentication is going to be "PLAIN"... You can specify many types like for example: "smtp_server.esmtp_features['auth'] = 'LOGIN PLAIN'".