I have the service principal set up in Azure portal with Mail.Send access, I have client_id, tenant_id, client_secret information of that service principal. I am using the below code to send email, but I am getting errors.
So, in the graph url, if I use https://graph.microsoft.com/v1.0/me
. I am getting authentication error. If use this url https://graph.microsoft.com/v1.0/users/{user_id}/sendMail
, in place of user_id
, I have tried the below options, but everything failed and given Invalid user error. Help me to resolve this.
for user_id, used the below options,
import requests
import json
import msal
# Define your Microsoft 365 email details
recipient_email = "receiver@example.com"
subject = "Email subject"
message_body = "Email message body"
# Your application (client) ID and client secret obtained from Azure AD
client_id = "90xxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
client_secret = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
# Tenant ID (directory ID)
tenant_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxx"
# Microsoft Graph API endpoint to send an email as the application (service principal)
graph_url = f"https://graph.microsoft.com/v1.0/users/{client_id}/sendMail"
# Create a confidential client application
app = msal.ConfidentialClientApplication(
client_id=client_id,
authority=f"https://login.microsoftonline.com/{tenant_id}",
client_credential=client_secret,
)
# Get an access token
result = app.acquire_token_for_client(scopes=["https://graph.microsoft.com/.default"])
access_token = result.get("access_token")
# Create the email message
email_message = {
"message": {
"subject": subject,
"body": {
"contentType": "Text",
"content": message_body,
},
"toRecipients": [
{
"emailAddress": {
"address": recipient_email
}
}
]
}
}
# Convert the message to JSON format
email_data = json.dumps(email_message)
# Send the email
response = requests.post(
graph_url,
headers={
"Authorization": "Bearer " + access_token,
"Content-Type": "application/json",
},
data=email_data,
)
# Check the response status code
if response.status_code == 202:
print("Email sent successfully!")
else:
print(f"Failed to send email. Status code: {response.status_code}")
print(response.text)
Invalid user error I got for using client_id or tenant_id or both combined,
**For client_id or tenant_id:**
Failed to send email. Status code: 404
{"error":{"code":"ErrorInvalidUser","message":"The requested user 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx' is invalid."}}
**For both combined:**
Failed to send email. Status code: 404
{"error":{"code":"ErrorInvalidUser","message":"The requested user 'app:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx@xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx' is invalid."}}
**For using graph url with /me:**
Failed to send email. Status code: 400
{"error":{"code":"BadRequest","message":"/me request is only valid with delegated authentication flow.","innerError":{"date":"2023-09-29T15:44:45","request-id":"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx","client-request-id":"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx"}}}
The error occurred as you are passing
client_id
ortenant_id
in graph url instead of user ID or UPN.
I registered one Azure AD application and granted Mail.Send
API permission of Application type as below:
When I ran your code in my environment by passing client_id
in graph url, I too got same error as below:
import requests
import json
import msal
# Define your Microsoft 365 email details
recipient_email = "receiver@example.com"
subject = "Email subject"
message_body = "Email message body"
# Your application (client) ID and client secret obtained from Azure AD
client_id = "xxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
client_secret = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
# Tenant ID (directory ID)
tenant_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxx"
# Microsoft Graph API endpoint to send an email as the application (service principal)
graph_url = f"https://graph.microsoft.com/v1.0/users/{client_id}/sendMail"
# Create a confidential client application
app = msal.ConfidentialClientApplication(
client_id=client_id,
authority=f"https://login.microsoftonline.com/{tenant_id}",
client_credential=client_secret,
)
# Get an access token
result = app.acquire_token_for_client(scopes=["https://graph.microsoft.com/.default"])
access_token = result.get("access_token")
# Create the email message
email_message = {
"message": {
"subject": subject,
"body": {
"contentType": "Text",
"content": message_body,
},
"toRecipients": [
{
"emailAddress": {
"address": recipient_email
}
}
]
}
}
# Convert the message to JSON format
email_data = json.dumps(email_message)
# Send the email
response = requests.post(
graph_url,
headers={
"Authorization": "Bearer " + access_token,
"Content-Type": "application/json",
},
data=email_data,
)
# Check the response status code
if response.status_code == 202:
print("Email sent successfully!")
else:
print(f"Failed to send email. Status code: {response.status_code}")
print(response.text)
Response:
To resolve the error, you need to pass either ID or UPN of user having active Office 365 license.
When I ran the same code by passing user's UPN in graph url, I got response like this:
import requests
import json
import msal
# Define your Microsoft 365 email details
recipient_email = "sri@xxxxx.onmicrosoft.com"
subject = "Demo mail"
message_body = "Hello Sri!"
# Your application (client) ID and client secret obtained from Azure AD
client_id = "xxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
client_secret = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
# Tenant ID (directory ID)
tenant_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxx"
# User's UPN
user_upn = "xxxx_rk@M3xxxxxxx.onmicrosoft.com"
# Microsoft Graph API endpoint to send an email as the application (service principal)
graph_url = f"https://graph.microsoft.com/v1.0/users/{user_upn}/sendMail"
# Create a confidential client application
app = msal.ConfidentialClientApplication(
client_id=client_id,
authority=f"https://login.microsoftonline.com/{tenant_id}",
client_credential=client_secret,
)
# Get an access token
result = app.acquire_token_for_client(scopes=["https://graph.microsoft.com/.default"])
access_token = result.get("access_token")
# Create the email message
email_message = {
"message": {
"subject": subject,
"body": {
"contentType": "Text",
"content": message_body,
},
"toRecipients": [
{
"emailAddress": {
"address": recipient_email
}
}
]
}
}
# Convert the message to JSON format
email_data = json.dumps(email_message)
# Send the email
response = requests.post(
graph_url,
headers={
"Authorization": "Bearer " + access_token,
"Content-Type": "application/json",
},
data=email_data,
)
# Check the response status code
if response.status_code == 202:
print("Email sent successfully!")
else:
print(f"Failed to send email. Status code: {response.status_code}")
print(response.text)
Response:
To confirm that, I checked the same in user's Outlook where mail sent successfully as below:
Here, you are authenticating as a service principal by generating access token using client credentials flow where there is no user interaction.
With this token, service principal will send mail on-behalf of specified user.