7
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]').
2
  • Have you been through the suggestions here? stackoverflow.com/questions/34045404/…. Especially double-checking your password and email address. Commented Mar 29, 2020 at 4:33
  • How to check if the email address was created as a shared mailbox? Commented Mar 30, 2020 at 5:28

6 Answers 6

10

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

Sign up to request clarification or add additional context in comments.

Comments

1

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

1 Comment

Thank you for your contribution. This should be a Comment to the referenced post. It does not qualify as an Answer as per SO guidelines (which are necessary for this platform to function well--it works differently than forums). Once you have enough reputation, you can comment anywhere. We appreciate your helpfulness and engagement. Welcome to SO. You can read more in the help section.
1

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!

1 Comment

Is there a way to add attachments to the email, using the OAuth method?
0

@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!

2 Comments

Hi I have also encountered the same issue, Admins can’t allow the basic authentication for SMTP_AUTH.. but i have to do it using python script which is an automated script. Can you provide some inputs on this?
Sorry, @VipendraSingh. No ideas how that could be done any way other than I described.
0

Microsoft announced that they will stop supporting Basic Auth Authentication (02.2025). More infos here: https://learn.microsoft.com/en-us/exchange/clients-and-mobile-in-exchange-online/authenticated-client-smtp-submission

Confidential Client Authentication

To use confidential client authentication:

Ensure API Permissions are set correctly on Azure.

  • Azure Portal > Azure Active Directory > App registrations.
  • Select app > API permissions.
  • Add a permission > APIs my organization uses > Office 365 Exchange Online.
  • Select Application Permissions (not Delegated).
  • Add SMTP.Send.
  • Click Grant Admin Consent.

First install Microsoft Authentication Library: pip install msal

To get a token:

import os

import dotenv

dotenv.load_dotenv()
import msal


def _get_token() -> str:
    AUTHORITY = f'https://login.microsoftonline.com/{os.environ["CLIENT_ID"]}'  # App registrations -> Endpoints
    SCOPES = ["https://outlook.office365.com/.default"]
    app = msal.ConfidentialClientApplication(
        client_id=os.environ["CLIENT_ID"],
        client_credential=os.environ["CLIENT_SECRET"],
        authority=AUTHORITY,
    )
    token_response = app.acquire_token_for_client(SCOPES)

    if "access_token" in token_response:
        access_token = token_response["access_token"]
        print("Access Token:", access_token)
        return access_token
    else:
        print("Failed to obtain access token:", token_response.get("error_description"))


if __name__ == "__main__":
    _get_token()

After obtaining the token, use it in XOAUTH2 SMTP Authentication.

import base64

import dotenv

dotenv.load_dotenv()
import smtplib
from scripts.debug_microsoft_confidential_client_login import _get_token


def _send_test_email():
    SMTP_SERVER = "smtp.office365.com"
    SMTP_PORT = 587
    FROM_EMAIL = "[email protected]"
    TO_EMAIL = "recipient-email"
    ACCESS_TOKEN = _get_token()

    auth_string = f"user={FROM_EMAIL}\x01auth=Bearer {ACCESS_TOKEN}\x01\x01"
    auth_string_base64 = base64.b64encode(auth_string.encode()).decode()

    server = smtplib.SMTP(SMTP_SERVER, SMTP_PORT)
    server.set_debuglevel(True)
    server.ehlo()
    server.starttls()
    server.ehlo()
    server.docmd("AUTH", "XOAUTH2 " + auth_string_base64)
    email_message = f"Subject: Test Email\nTo: {TO_EMAIL}\nFrom: {FROM_EMAIL}\n\nThis is a test email sent via XOAUTH2 SMTP."
    server.sendmail(FROM_EMAIL, TO_EMAIL, email_message)
    server.quit()
    print("Email sent successfully!")


if __name__ == "__main__":
    _send_test_email()

Public Client Authentication

It is also possible to use Public Client instead.

Simply use this scope instead.

SCOPES = ["https://outlook.office365.com/SMTP.Send"]

Ensure API Permissions are set correctly on Azure.

  • Azure Portal > Azure Active Directory > App registrations.
  • Select app > API permissions.
  • Add a permission > Microsoft Graph > Delegated Permissions.
  • Select Application Permissions (not Delegated).
  • Add SMTP > SMTP.Send.

Comments

0

When this issue first came up a few years ago I decided to take a different approach, and wrote a proxy that sits between your IMAP/POP/SMTP client and the OAuth email provider. This way, you don't need to modify your client code, and only have to handle interactive OAuth requests once per account. You can find it here: https://github.com/simonrob/email-oauth2-proxy.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.