How to send SMTP email for office365 with python using tls/ssl
Asked Answered
C

6

35

I am trying to send an email from my office365 corporate account using python. I am new to python. This code previously worked when using my hotmail account, however now that I have a need to send confidential information, I must use my corporate email.

I have tried a couple things.

  • Verified that my username and password is correct.
  • Used both python2 and python3. Both give the same error: 535 5.7.3 Authentication unsuccessful
  • I previously was using mailserver.starttls() when I got the above error, then after some research, I tried to pass a
    certificate.mailserver.starttls(certfile='office365.cer')

I am unclear on the certificate part, but my steps include, looking online to find out how to export a certificate. Using chrome browser, microsoftonline.com has a chain certificate. I can export the root and the level just below the root but not the last level. I dont know how to pass both of these files, so I have simply passed the root certificate. At this point I get the error: ssl.SSLError: [SSL] PEM lib (_ssl.c:3309)

i got stuck at this point. Any help is appreciated. Code used below

import smtplib

mailserver = smtplib.SMTP('smtp.office365.com',587)
mailserver.ehlo()
mailserver.starttls(certfile='office365.cer')
mailserver.ehlo()
mailserver.login('[email protected]', 'password')
mailserver.sendmail('[email protected]','[email protected]','python email')
mailserver.quit()
Calamitous answered 11/9, 2017 at 17:3 Comment(0)
E
51

Well, you are almost there. The following code will do the trick:

import smtplib

mailserver = smtplib.SMTP('smtp.office365.com',587)
mailserver.ehlo()
mailserver.starttls()
mailserver.login('[email protected]', 'password')
#Adding a newline before the body text fixes the missing message body
mailserver.sendmail('[email protected]','[email protected]','\npython email')
mailserver.quit()

Use the following links for more information:

Python: Send Email via Office 365

https://docs.python.org/3/library/smtplib.html

https://gist.github.com/jasonjoh/3ec367594c3fa662ee983a617bdc7deb

Echinate answered 29/4, 2018 at 12:47 Comment(9)
NOTICE: the use of smtplib.SMTP() instead of smtplib.SMTP_SSL() ! With smtplib.SMTP_SSL() you will still get the "[SSL: WRONG_VERSION_NUMBER]" error. replacing it with smtplib.SMTP() worked for me.Cammack
the row mailserver.ehlo() is not necessary. From starttls() documentation: "If there has been no previous EHLO or HELO command this session, this method tries ESMTP EHLO first."Ironlike
Perhaps this is obvious, but if your corporate account requires multi-factor authentication, username and pw are inadequate to authenticate.Healey
Hi, I have a strange issue, the above sequence of code works perfectly when I am executing it on the Python interpreter terminal. However, if I put the code into a .PY file and run it on VS Code or the Mac terminal, it hangs! :( Any pointers, please?Weigand
@DeepakV It is probably something else in your code. This is just a fix for the original question. You should post a new thread with your debuging results.Echinate
No @GalSilberman I promise, it is the same code copy-pasted into a .py file :( That is why I am splitting my hair. When I run it as a python program, the code is stuck at the following place: mailserver = smtplib.SMTP('smtp.office365.com',587) the terminal continues to put out a blank line and the execution waits...Weigand
Since this is the first line, do you have any pointers as to if I can enable some option in Python like python3 -debug send_email.py to print what is happening when I call mailserver = smtplib.SMTP('smtp.office365.com',587) ?Weigand
It has been a few years since the original answer. Things might have changed in the package. Try to follow the examples from docs.python.org/3/library/smtplib.html on how to use the package.Echinate
It got interesting, I found that a Python program can be debugged using python3 -m pdb <file.py> I did that and did evaluation step by step, then it is working if I step inside the function smtplib.SMTP('smtp.office365.com',587), however, if I use the command 'n' to step over the function, it still has the same behavior - it gets stuck.Weigand
B
16

I found a library that it's working for me:

https://github.com/O365/python-o365

https://pypi.python.org/pypi/O365

Install it using PIP and then:

from O365 import Message
o365_auth = ('[email protected]','YourPassword')
m = Message(auth=o365_auth)
m.setRecipients('[email protected]')
m.setSubject('I made an email script.')
m.setBody('Talk to the computer, cause the human does not want to hear it any more.')
m.sendMessage()
Barringer answered 20/11, 2017 at 19:22 Comment(6)
I just want to point out for anyone seeing this in the future; while this library works, it does not use SMTP. It uses the Microsoft Outlook/Graph API in order to access your Office 365 account and send emails. See Gal's answer below if you want to use SMTP.Snare
@Snare for us mere mortals, can you explain the difference, what will the use of this code do differently from the above?Slype
@Datanovice SMTP is a protocol for sending emails, with smtplib you are sending the email directly to the Office365 mail server using the SMTP protocol. With nacho-parra's answer you are using a Python module (O365) which uses sends an HTTP request to the Microsoft Graph API which then sends the email. It's basically same result (email gets sent) but different methods. The original question was asking about sending using SMTP so gal-silberman's answer is the one that actually shows how to accomplish that.Snare
@Snare you are the man, very succinct and to the point edition, cheers. I wonder which is more expensive resource wise, or if the api has a limit on emails.Slype
@Datanovice SMTP is definitely simpler for sending a basic email, the graph API allows you to do a lot more besides just send email though. As far as rate limits for the API, they are pretty generous but they do have some throttling. Only time I ran into throttling was when retrieving like 10,000+ messages from an email inbox. SMTP servers can also have their own limits set by the provider though, see here.Snare
@Snare that is very relevant for my use case, I'm looking at a way to automate compliance via a few python scripts and email this out to my store managers, around 450 in total. I think I'll be fine with the limits.Slype
A
10

For me the answer provided by @Prometheus was giving "RuntimeError: No auth token found . Authentication Flow needed" as mentioned by a comment. It maybe because of my company email with 2fa enabled. So I had to follow steps provided in https://github.com/janscas/pyo365#authentication and https://pypi.org/project/O365/#authentication

To work with oauth you first need to register your application at Microsoft Application Registration Portal.

  1. Login at Microsoft Application Registration Portal
  2. Create an app, note your app id (client_id)
  3. Generate a new password (client_secret) under "Application Secrets" section Under the "Platform" section, add a new Web platform and set https://login.microsoftonline.com/common/oauth2/nativeclient" as the redirect URL
  4. Goto API permissions > Add permission > Microsoft Graph > Delegated permissions, add the permissions below:
    • IMAP.AccessAsUser.All

    • Mail.Send

    • POP.AccessAsUser.All

    • User.Read

    • SMTP.Send

    • offline_access # If you want refresh token to be avaialble in o365_token.txt. Otherwise you need to get access token every 1hr. enter image description here

  5. Run below python script to obtain access token which will be stored in file called o365_token.txt in current directory
from O365 import Account

scopes =  ["IMAP.AccessAsUser.All", "POP.AccessAsUser.All", "SMTP.Send", "Mail.Send", "offline_access"]

account = Account(credentials=('client_id_of_azureapp', 'client_secret_of_azureapp'))
result = account.authenticate(scopes=scopes)  # request a token for this scopes
  1. Use below script to send email by providing the authtoken file generated in previous step.
from O365 import Account
from O365.utils.token import FileSystemTokenBackend
tk = FileSystemTokenBackend(token_path=".", token_filename="o365_token.txt")

credentials = ('client_id', 'client_secret')

account = Account(credentials, auth_flow_type = 'public',token_backend=tk)
m = account.new_message()
m.to.add('[email protected]')
m.subject = 'Testing!'
m.body = "George Best quote: I've stopped drinking, but only while I'm asleep."
m.send()

Note: If you have admin consent or you are administrator or azure account you can skip the step 4,5,6 and use below code directly.

from O365 import Account
from O365.utils.token import FileSystemTokenBackend
tk = FileSystemTokenBackend(token_path=".", token_filename="o365_token.txt")

credentials = ('client_id', 'client_secret') # from step 2,3
account = Account(credentials, auth_flow_type = 'credentials', tenant_id="your_app_tenant_id") # tenant_id (required) available just below client_id in azure

if account.authenticate():
    print('Authenticated!')

mailbox = account.mailbox("[email protected]") # Your email (required) from which you want to send email (your app should have permission to this email)
m = mailbox.new_message()
m.to.add('[email protected]')
m.subject = 'Testing!'
m.body = "George Best quote: I've stopped drinking, but only while I'm asleep."
m.send()
Actinochemistry answered 16/2, 2022 at 19:19 Comment(0)
M
3

The code has slightly changed. The above code won't work. Please use the below code. Reference

from O365 import Account

credentials = ('client_id', 'client_secret')

account = Account(credentials)
m = account.new_message()
m.to.add('[email protected]')
m.subject = 'Testing!'
m.body = "George Best quote: I've stopped drinking, but only while I'm asleep."
m.send()
Metry answered 11/9, 2019 at 6:27 Comment(3)
I get the below error while using this.. "RuntimeError: No auth token found. Authentication Flow needed"Houghton
@ShashiShankarSingh did you setup the client token in Azure?Brokerage
@Shashi Shankar Singh See my answer https://mcmap.net/q/424320/-how-to-send-smtp-email-for-office365-with-python-using-tls-ssl I was able to resolve this issue.Actinochemistry
C
3

Most likely, the problem is not in your code, but in the Exchange Online configuration.

I bet 535 5.7.3 Authentication unsuccessful is thrown because authenticated SMTP (SMTP AUTH protocol) is disabled in your Exchange Online organization.

Here you can find my answer explaining how you can enable SMTP AUTH for the user you are sending emails from. You have to be an Office 365 org admin to do that, or you can ask your administrator for help.

After that mailserver.starttls() should work. Notice that you don't need to specify a certificate in that call.

Close answered 28/6, 2020 at 22:49 Comment(0)
B
1

The following is a more concise alternative to @Gal's answer that worked for me:

    msg = MIMEText(f'Pubblicati i risultati per la ricerca: `{search_result}`\n\nPer maggiori dettagli, visita: {url}')  # replace with your email message
    msg['Subject'] = f"Pubblicazione criteri di valutazione {search_result.split(' ')[2]}"
    msg['From'] = from_email
    msg['To'] = to_email
    with smtplib.SMTP('smtp.office365.com', 587) as smtp:
        smtp.starttls()
        smtp.login(from_email, password)
        smtp.send_message(msg)
Braiding answered 3/5, 2023 at 8:26 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.