0

I've been fighting with OAuth and MSAL stuff all day today in python. I have a simple application, users fill my form, click submit and then I write their stuff to a database. I want to send them an email saying "I wrote your stuff to the database" I want that email to come from my system account which is an AZAD account.

I feel like I should be able to somehow get a token to interact with the graph API using ONLY the name, password then send an email. However I keep running into walls.

I can't get ADMIN stuff for my app to just send email as ANY user. All I'm trying to do is send an email from my system account. I CAN do this manually in outlook application on my desktop but I can't do it from my python IDE.

I want to do this using HTTP requests if possible

import requests_oauthlib
from requests_oauthlib import OAuth2Session
from oauthlib.oauth2 import LegacyApplicationClient
tenant_id = tid
client_id = cid
client_secret = cs
uri =f"https://login.microsoftonline.com/{tenant_id}/oauth2/v2.0/token"
username=un
password=pw

#DOES NOT WORK says the user account doesn't exist
oauth = OAuth2Session(client=LegacyApplicationClient(client_id=client_id))
token = oauth.fetch_token(
    token_url=uri
    ,client_id=client_id
    ,client_secret=client_secret
    ,username=username
    ,password=password
    ,scope="Mail.Send"
    )

#THIS DOES WORK BUT I CANT GET IT TO EMAIL
client = BackendApplicationClient(client_id=client_id)
oauth = OAuth2Session(client=client)
token = oauth.fetch_token(
    token_url=uri
    ,client_id=client_id
    ,client_secret=client_secret
    ,scope=["https://graph.microsoft.com/.default"]
    )

recipient='[email protected]'
subject="test_subject"
body="test_body"
request_body = {
    'message': {
        # recipient list
        'toRecipients': [
            {
                'emailAddress': {
                    'address': recipient
                }
            }
        ],
        # email subject
        'subject': subject,
        "body": {
            "contentType": "html",
            "content": body
        },
        'importance': 'normal',

    }
}
headers = {
    'Authorization': 'Bearer ' + token['access_token']
}

GRAPH_ENDPOINT = 'https://graph.microsoft.com/v1.0'
endpoint = GRAPH_ENDPOINT + '/me/sendMail'

response = requests.post(endpoint, headers=headers, json=request_body)
response.raise_for_status()  # Raise an exception if request fails

if response.status_code == 202:
    print(f"Email sent to: {recipient}")
else:
    print(f"Email not sent to: {recipient}")


2
  • Could you confirm whether the user account you are using to send email is personal Microsoft account or local user account ending with .onmicrosoft.com?
    – Sridevi
    Commented Oct 19, 2024 at 3:31
  • Hi @rrz Any update?
    – Sridevi
    Commented Nov 13, 2024 at 12:16

1 Answer 1

0

Alternatively, you can make use of msal library for generating access token using username and password.

Initially, I registered one application and granted Mail.Send Delegated permission with admin consent that allows only signed-in user to send mail:

enter image description here

While using username password flow, make sure to enable public client flow option to avoid client secret usage:

enter image description here

Now, I ran below sample python code to create access token with username password and got response like this:

#pip install msal
import msal
import requests

tenant_id = 'tenantId'
client_id = 'appId'
username = '[email protected]'  # System account username (email)
password = 'xxxxxxx'

authority = f"https://login.microsoftonline.com/{tenant_id}"
scope = ["https://graph.microsoft.com/Mail.Send", "https://graph.microsoft.com/User.Read"]

app = msal.PublicClientApplication(client_id, authority=authority)

result = app.acquire_token_by_username_password(
    username=username,
    password=password,
    scopes=scope
)

if "access_token" in result:
    access_token = result['access_token']
    print("Token acquired successfully")

    # Prepare the email
    recipient = '[email protected]'
    subject = "Acknowledgement"
    body = "I wrote your stuff to the database"
    request_body = {
        'message': {
            'toRecipients': [
                {'emailAddress': {'address': recipient}}
            ],
            'subject': subject,
            "body": {"contentType": "html", "content": body},
            'importance': 'normal',
        }
    }

    headers = {
        'Authorization': 'Bearer ' + access_token,
        'Content-Type': 'application/json'
    }

    # Send email on behalf of the user (using '/me/sendMail' since this is a delegated flow)
    GRAPH_ENDPOINT = 'https://graph.microsoft.com/v1.0'
    endpoint = f"{GRAPH_ENDPOINT}/me/sendMail"

    # Send the email
    response = requests.post(endpoint, headers=headers, json=request_body)

    # Check for success
    if response.status_code == 202:
        print(f"Email sent to: {recipient}")
    else:
        print(f"Failed to send email: {response.status_code}, {response.text}")
else:
    print("Failed to acquire token:", result.get("error"), result.get("error_description"))

Response:

enter image description here

To confirm that, I checked the same in Sent Items of user where mail sent successfully as below:

enter image description here

2
  • My organization has an entirely separate smt server that I can get at using smtplib in python. so I ended up doing that with my system account credential. I'd like to come back to graph UI but I'm finding the delegated vs admin required permissions to be an enterprise grade pain. The issue for me is the "send email" scope is even locked down without a consent based login even for my system account. Since that's entirely headless I can't clear that hurdle either.
    – rrz
    Commented Dec 15, 2024 at 19:41
  • Could you confirm whether your requirement is to send mail without login of user account?
    – Sridevi
    Commented Dec 16, 2024 at 2:38

Not the answer you're looking for? Browse other questions tagged or ask your own question.