pythonaiosmtpd

python aiosmtpd server with basic authentication


Im trying to create an aiosmtpd server to process emails received. It works great without authentication, yet i simply cannot figure out how to setup the authentication. I have gone through the documents and searched for examples on this.

a sample of how im currently using it:

from aiosmtpd.controller import Controller

class CustomHandler:
    async def handle_DATA(self, server, session, envelope):
        peer = session.peer
        mail_from = envelope.mail_from
        rcpt_tos = envelope.rcpt_tos
        data = envelope.content         # type: bytes
        # Process message data...
        print('peer:' + str(peer))
        print('mail_from:' + str(mail_from))
        print('rcpt_tos:' + str(rcpt_tos))
        print('data:' + str(data))
        return '250 OK'

if __name__ == '__main__':
    handler = CustomHandler()
    controller = Controller(handler, hostname='192.168.8.125', port=10025)
    # Run the event loop in a separate thread.
    controller.start()
    # Wait for the user to press Return.
    input('SMTP server running. Press Return to stop server and exit.')
    controller.stop()```

which is the basic method from the documentation.

could someone please provide me with an example as to how to do simple authentication?

Solution

  • Alright, since you're using version 1.3.0, you can follow the documentation for Authentication.

    A quick way to start is to create an "authenticator function" (can be a method in your handler class, can be standalone) that follows the Authenticator Callback guidelines.

    A simple example:

    from aiosmtpd.smtp import AuthResult, LoginPassword
    
    auth_db = {
        b"user1": b"password1",
        b"user2": b"password2",
        b"user3": b"password3",
    }
    
    # Name can actually be anything
    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
        # If we're using a set containing tuples of (username, password),
        # we can simply use `auth_data in auth_set`.
        # Or you can get fancy and use a full-fledged database to perform
        # a query :-)
        if auth_db.get(username) == password:
            return AuthResult(success=True)
        else:
            return AuthResult(success=False, handled=False)
    

    Then you're creating the controller, create it like so:

        controller = Controller(
            handler,
            hostname='192.168.8.125',
            port=10025,
            authenticator=authenticator_func,  # i.e., the name of your authenticator function
            auth_required=True,  # Depending on your needs
        )