Our web application sends nicely formatted messages (including some embedded images) through python's smtplib. The messages are generated with python's email.message.EmailMessage class, as shown in the code below.
At the start of October, Microsoft deprecated support for Basic Auth in their Office365 Email accounts. We had been using Basic Auth and now needed to find a new solution. After struggling with getting OAuth2 working for some time, we decided to refactor and use the Microsoft Graph API instead.
Oddly, even though most of the examples on the Graph API site includes multi-lingual (HTTP / C# / Javascript / PHP etc) examples, the one for sending an email with a MIME format (Examlpe 4) only has an HTTP example.
We would like to know if it's possible to send the email we had built using python.email.EmailMessage using the Graph API, and if yes, how to do it.
Below is some example code, showing what we did before and what we're trying to get right now.
when we run the code, we get the error
'{"error":{"code":"RequestBodyRead","message":"Requested value \'text/plain\' was not found."}}'
import smtplib
from email.utils import formatdate
from email.message import EmailMessage
import requests
server = 'smtp.office365.com' # for exampler
port = 587 # for example
from_mail = 'levrai@ninjane.er'
to_mail = 'desti@nati.on'
subject = 'Demo sending the old way!'
password = 'not_so_Secur3!'
message_parts = ['Hi sir', 'This is a demo message.', 'It could help others to help me, and possbily others too.']
# the below function builds up the nice message based on an html template
text_msg, html_msg, cids, locs = doc_mail_from_template(message_parts)
msg = EmailMessage()
msg.set_content(text_message)
msg.add_alternative(html_msg, subtype='html')
msg['From'] = from_mail
msg['To'] = to_mail
msg['Date'] = formatdate(localtime=True)
msg['Subject'] = subject
# now embed images to the email
for loc, cid in zip(locs, cids):
with open(loc, 'rb') as img:
maintype, subtype = guess_type(img.name)[0].split('/') # know the Content-Type of the image
msg.get_payload()[1].add_related(img.read(), maintype=maintype, subtype=subtype, cid=cid) # attach it
if date_now < '2022-10-01': # before, we could do this
with smtplib.SMTP(server, port) as smtp:
smtp.starttls(context=context)
smtp.login(from_mail, password)
smtp.sendmail(from_mail, [to_mail, ], msg.as_string())
else: # now we must do this
client_id = 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxx8c5'
client_secret = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-xxb96'
tenant_id = 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxx973'
userId = "levrai@ninjane.er"
authority = f"https://login.microsoftonline.com/{tenant_id}"
scopes = ["https://graph.microsoft.com/.default"]
app = msal.ConfidentialClientApplication(client_id=client_id, client_credential=client_secret, authority=authority)
result = app.acquire_token_silent(scopes, account=None)
if not result:
result = app.acquire_token_for_client(scopes=scopes)
# setup message:
email_msg = {'Message': {'Subject': subject,
'Body': {
'ContentType': 'text/plain', 'Content': e_message.as_string() }, # what do i put here?
'ToRecipients': [{'EmailAddress': {'Address': to_mail}}]
},
'SaveToSentItems': 'true'}
endpoint = f'https://graph.microsoft.com/v1.0/users/{from_user}/sendMail'
r = requests.post(endpoint, json=email_msg,
headers={'Authorization': 'Bearer ' + result['access_token'], "Content-Type": "application/json"})