Nandu
Nandu

Reputation: 71

SMTP Authentication error while while sending mail from outlook using python language

import smtplib


smtpObj = smtplib.SMTP('smtp.office365.com', 587)

smtpObj.ehlo()

smtpObj.starttls()

smtpObj.login('[email protected]', ' abcde')

smtpObj.sendmail('[email protected]', '[email protected]', 'Subject: So long.\nDear Alice, so long and thanks for all the fish. Sincerely, Bob')

{}

smtpObj.close()

The error I am getting

SMTPAuthenticationError: (535, b'5.7.3 Authentication unsuccessful [BM1PR01CA0150.INDPRD01.PROD.OUTLOOK.COM]').

Upvotes: 7

Views: 27406

Answers (4)

r8nn1e
r8nn1e

Reputation: 11

There is a way to send the email without compromising your security:
Given that Outlook (and Microsoft 365) generally encourages secure practices, the recommended way to send emails without reducing security is by using the Microsoft Graph API with OAuth 2.0 for authentication.

Sending Outlook Emails with Python Without Disabling Security Defaults

There is a way to send the email without compromising your security. It will require using secure authentication methods. Given that Outlook (and Microsoft 365) generally encourages secure practices, the recommended way to send emails without reducing security is by using the Microsoft Graph API with OAuth 2.0 for authentication.

Follow these Steps

  1. Set Up an Azure App Registration:

    • Log in to the Azure Portal.
    • Go to "Mircosoft Entra ID" (formally "Azure Active Directory") and find "App registrations".
    • Create a new registration, providing a name and redirect URI (use a placeholder for local testing, like http://localhost).
    • Take note of the "Application (client) ID" and "Directory (tenant) ID".
  2. Configure Permissions for the App:

    • In your new app registration, go to "API permissions".
    • Click "Add a permission" and choose "Microsoft Graph".
    • Add "Application permissions" for sending emails. You might add Mail.Send permission, among others, depending on your requirements.
    • Click "Grant admin consent" to ensure the app has appropriate permissions.
  3. Set Up Authentication with OAuth 2.0:

    • Go to "Certificates & secrets" in your app registration and create a new "client secret". The client secret Value will act as the password for your app, so make sure to store it securely.
  4. Use Microsoft Authentication Library (MSAL) in Python:

    • Install msal (Microsoft Authentication Library) in your Python environment:
      pip install msal
      
  5. Write Python Code to Send an Email:

    • Use the msal library to get an access token, then use this token to send an email via Microsoft Graph. Here's a simplified example of how to do this:
      import msal
      import requests
      
      # Set up your application details
      client_id = "YOUR_CLIENT_ID"
      client_secret = "YOUR_CLIENT_SECRET_VALUE"
      tenant_id = "YOUR_TENANT_ID"
      authority = f"https://login.microsoftonline.com/{tenant_id}"
      scope = ["https://graph.microsoft.com/.default"]
      
      # Get an access token
      app = msal.ConfidentialClientApplication(authority=authority, client_id=client_id, client_credential=client_secret)
      result = app.aquire_token_for_client(scope)
      access_token = result['access_token']
      
      # Define email data
      email = {
          "message": {
              "subject": "Email Subject!",
              "body": {
                  "contentType": "Text",
                  "content": " EmailContent"
              },
              "toRecipients": [
                  {
                      "emailAddress": {
                          "address": "RECIPIENT_EMAIL"
                      }
                  }
              ]
          }
      }
      
      # Send the email
      sender_userid = "EMAIL_USER_ID"
      endpoint = f'https://graph.microsoft.com/v1.0/users/{sender_userid}/sendMail'
      headers = {
          "Authorization": f"Bearer {access_token}",
          "Content-Type": "application/json"
      }
      
      response = requests.post(endpoint, json=email, headers=headers)
      
      if response.status_code == 202:
          print("Email sent successfully")
      else:
          print(f"Failed to send email: {response.status_code}")
      

This did it for me!

Upvotes: 0

Brian
Brian

Reputation: 25

Thanks @wombatonfire - very helpful - I have a small addition to your answer.

I tried for a few hours to setup up multiple email addresses to send mail from a Python script; all of the accounts I was working with showed Authenticated SMTP to be enabled on the Active User page, but I was still getting authentication errors.

Not until I deselected and re-selected the "Authenticated SMTP" checkbox for each account did the script work.

Thanks

Upvotes: 1

Dan Farfan
Dan Farfan

Reputation: 1

@wombatonfire gave a terrific answer, but if for any reason those steps aren't possible (as was my situation), the following solved the OP problem for me.

I'm on a Mac. Can't get to admin center. Had to use powershell. Also, powershell on my Mac had to connect to MSFT exchange server before I could change the setting for my mailbox. The following makes email work as designed.

There is a critical hoop to jump through to make this all work. To connect your Mac to MSFT Exchange server, your Mac must use TLS1.2 from/via/through OpenSSL1.0. OpenSSL1.1 is a no go.

Get a terminal window on your Mac:

Click LaunchPad, type "term", click Terminal

In the terminal window, check what version(s) of OpenSSL are on your Mac:

>ls -al /usr/local/Cellar/openssl*

See which what version is active:

>openssl version -a

OpenSSL 1.1.* is bad. OpenSSL 1.0.* is good.

NORMALLY, you can use brew to switch versions of a package is active with:

>brew switch openssl 1.0.2s
>brew link --overwrite openssl

But I got this error: Warning: Refusing to link macOS provided/shadowed software: openssl. So I had to get tricky.

Change PATH environment variable (just in this terminal session, not permanently).

>PATH=/usr/local/Cellar/openssl/1.0.2s/bin:$PATH

Now the check, shows good version:

>openssl version -a

Next, I followed steps to install powershell documented here: https://learn.microsoft.com/en-us/powershell/scripting/install/installing-powershell-core-on-macos?view=powershell-7

Now, open powershell as admin.

>sudo su - root
<your mac password>
root>pwsh 

At the powershell prompt, double check your powershell version. Version 7 is needed.

>$host.version

I have: 7.0.3 Revision -1

Check what modules are installed in powershell:

>Get-Module -ListAvailable

If "PowerShellGet" is not listed, install it:

>Install-Module -Name PowerShellGet -Force

This next step is critical to success on the Mac. Only the latest "preview version 2.0.4" of "ExchangeOnlineManagement" package is going to work on Mac.

I don't know if this is needed, but I uninstalled the released version of "ExchangeOnlineManagement" package with:

>Uninstall-Module -Name ExchangeOnlineManagement -RequiredVersion 2.0.3

If preview version not present, install it:

>Install-Module -Name ExchangeOnlineManagement -AllowPrerelease -Force

One last detail to take care of. Tell powershell what version of TLS you want "ExchangeOnlineManagement" package to use:

>[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12

Finally, it's time to connect to the mothership: (Again, this is trickier on a Mac than in Windows, probably).

>Connect-ExchangeOnline -UserPrincipalName youremail@yourdomain

The above command will try to open a browser to a special authentication page. At least on my Mac, it couldn't. So:

COPY the giant link that gets displayed in the powershell window

PASTE the giant link into a web browser (I used Safari).

After you enter your Exchange credentials on that browser page, your powershell will show a progress bar for a short time, then magically be connected to MSFT Exchange server!

And the last step to set the SMTP setting on the mailbox you want to use:

Set-CASMailbox -Identity youremail@yourdomain -SmtpClientAuthenticationDisabled $false

Lastly, apparently it's important to always explicitly disconnect (before closing the terminal window):

>Disconnect-ExchangeOnline

That's the ballgame. You are changing the "disabled" setting to false for each/any/all mailboxes you want to send email from.

Now the fully documented, oft repeated python code seen in the OP will use SMTP and TLS to send email via MSFT Exchange (until something else breaks it all again :-O ).

Enjoy!

Upvotes: 0

wombatonfire
wombatonfire

Reputation: 5410

Most likely, the authenticated SMTP (SMTP AUTH protocol) is disabled in your Exchange Online organization.

SMTP AUTH can be enabled/disabled on the organization level, or per-mailbox. Because SMTP AUTH only uses basic authentication, Microsoft recommends to disable it on the organization level and enable it only for individual accounts that still require it.

If security defaults are enabled in the organization, then SMTP AUTH is disabled.

SMTP AUTH can be enabled in Microsoft 365 admin center or using Exchange Online Powershell.

To make it simple, to enable SMTP AUTH for a single account:

  1. Go to the Microsoft 365 admin center (https://admin.microsoft.com/) > Users > Active users.
  2. Select the user you are going to send emails from, and go to the Mail tab.
  3. In the Email apps section click Manage email apps.
  4. Enable Authenticated SMTP and click Save changes.

After that you should be able to authenticate using the respective account.

Important: You need admin rights in your Office 365 organization to do that. Otherwise, ask your O365 org admin for help.

Further details: https://learn.microsoft.com/exchange/clients-and-mobile-in-exchange-online/authenticated-client-smtp-submission

Upvotes: 10

Related Questions