Scenario:
I’m developing a software platform where users can send emails. They provide their authenticated Outlook address and the recipient’s email. My platform aims to send the email on their behalf.
Configuration:
I registered an app in Entra ID with “Accounts in any organizational directory (Any Microsoft Entra ID tenant – Multitenant) and personal Microsoft accounts (e.g. Skype, Xbox)” access.
The “Mail.Send” application permission is granted by the admin.
Challenges:
- Sending emails from addresses outside the tenant results in an
“Invalid User” error. - Sending from within-tenant addresses results in an “Inbox Not
Enabled” error. (Enabling my inbox for tenant users might work, but
what about external users?)
Question:
Is it possible to achieve true “send on behalf” functionality for both internal and external Outlook users using Microsoft Graph (Entra ID) with the “Mail.Send” permission?
Code:
require('isomorphic-fetch');
const { ConfidentialClientApplication } = require('@azure/msal-node');
const send = async (sender, data) => {
const EMAIL_URL = `https://graph.microsoft.com/v1.0/users/${sender.microsoft_email}/sendMail`;
try {
const msalConfig = {
auth: {
clientId: process.env.outlook_client_id,
authority: `https://login.microsoftonline.com/${process.env.outlook_tenant_id}`,
clientSecret: process.env.outlook_client_secret,
},
};
const cca = new ConfidentialClientApplication(msalConfig);
async function getAccessToken() {
const response = await cca.acquireTokenByClientCredential({
scopes: [
'https://graph.microsoft.com/.default',
],
});
return response.accessToken;
}
const accessToken = await getAccessToken();
const res = await fetch(EMAIL_URL, {
method: 'POST',
headers: {
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
message: {
subject: 'Testing',
toRecipients: [
{
emailAddress: {
address: data.to
}
}
],
body: {
content: 'Content',
contentType: 'Text'
}
}
})
});
if (!res.ok) {
const err = await res.text();
throw new Error('Error while sending ', err);
}
return { success: true, error: false };
}
catch (error) {
console.error(error);
return { success: false, error: true, message: "Error in Outlook Function", log: error };
}
}
Additional Notes:
I understand security limitations around impersonation.
I’m open to alternative secure approaches for sending emails on user consent.
I tried all the steps above
Faizan Shaikh is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
2