Reputation: 99
I am using nodemailer
to send email in my nodejs application.
var payload = { auth:
{
user: smtpuser,
pass: smtppass
},
to : toAddr,
from : emailfrom,
cc : ccAddr,
subject : subject,
html : content,
attachments: attachments
};
var transporter = nodemailer.createTransport(
{ host: payload.host || 'smtp.office365.com', // Office 365 server
port: payload.port || 587, // secure SMTP
secure:payload.secure || false, // false for TLS - as a boolean not string - but the default is false so just remove this completely
auth: payload.auth,
debug: true,
tls: payload.tls || {ciphers: 'SSLv3'}
});
transporter.sendMail(payload, function (error, info) {
if (error) {
return console.log(error);
}
updateMessage(updatedMsg);
});
I started getting this error:
Error: Invalid log in: 535 5.7.3 Authentication unsuccessful [SN4PR0601CA0002.namprd06.prod.outlook.com]
It seems my team has now disabled basic authentication.
I need to implement modern authentication(Oauth2) to be able to send mails via nodemailer
using the outlook id.
Does anyone have any idea about this? What configuration(code) changes will that require ?
Upvotes: 8
Views: 13658
Reputation: 2994
Microsoft is moving to discourage direct SMTP protocol access (regardless whether auth is basic or modern), especially so for automated scripts/jobs. The proposed best practice (for Office365) is to stop using SMTP protocol and instead use the Microsoft Graph API to send emails.
NodeMailer (as of Oct-2022) has transports for SMTP, sendmail, SES - no support yet for Microsoft Graph transport. [Feature Request].
Javascript/Node10+, Tested ~Nov 2022. Heavily sourced from Bogdan Le's answer
let tenantID = "" // Get from Azure App Registration
let oAuthClientID = "" // Get from Azure App Registration
let clientSecret = "" // Get from Azure App Registration
let oAuthToken; // declared, gets defined if successfully fetched
let userFrom = "[email protected]"
let msgPayload = {
//Ref: https://learn.microsoft.com/en-us/graph/api/resources/message#properties
message: {
subject: 'Test',
body: {
contentType: 'HTML',
content: 'Test123'
},
toRecipients: [{emailAddress: {address: '[email protected]'}}]
}
};
const axios = require('axios'); //using axios as http helper
await axios({ // Get OAuth token to connect as OAuth client
method: 'post',
url: `https://login.microsoftonline.com/${tenantID}/oauth2/token`,
data: new URLSearchParams({
client_id: oAuthClientID,
client_secret: clientSecret,
resource: "https://graph.microsoft.com",
grant_type: "client_credentials"
}).toString()
})
.then(r => oAuthToken = r.data.access_token)
await axios ({ // Send Email using Microsoft Graph
method: 'post',
url: `https://graph.microsoft.com/v1.0/users/${userFrom}/sendMail`,
headers: {
'Authorization': "Bearer " + oAuthToken,
'Content-Type': 'application/json'
},
data: msgPayload
})
Appendix 1: Create Azure oAuth App for sending emails
Create an Azure App Registration
Add mail sending permission: Azure App Registration Admin > API permissions > Add permission > Microsoft Graph
> Application
permissions > Mail.Send
WARNING: You will want to limit access of the app registration to specific mailboxes using application access policy. By default its tenant wide - allows impersonation of any user's mailbox!
Grant Admin Consent to permission to your newly created Mail.Send
permission (likely requires Global Admin)
Create Application Password: Azure App Registration Admin > Certificates and secrets > Client Secrets -> New client secret (note the generated secret)
Appendix 2: Limit Mail.Send
permission
Office 365 Admin: Create a mail-enabled security group (note the group name)
Add Members to that group: Add user(s) whose mailbox you want your App Registration to be able impersonate via Mail.Send
.
Create (and test) Application Access Policy, via Powershell:
##Variables - Manually define these:
$AppClientID="" #ClientID from Azure App Registration
$Group="" #Name of Office365 group
$ForbiddenMailbox="" #Address of mailbox AppRegistration should NOT have permission to
$AllowedMailbox="" #Address of mailbox AppRegistration should have permission to
##Connect to Exchange
Install-Module -Name ExchangeOnlineManagement
Connect-ExchangeOnline
##Create ApplicationAccessPolicy
New-ApplicationAccessPolicy -AppId $AppClientID -PolicyScopeGroupId $Group -AccessRight RestrictAccess -Description "Limit AppRegistration mailbox permissions to subset of users"
##Test ApplicationAccessPolicy - Denied Scenario
Test-ApplicationAccessPolicy -Identity $ForbiddenMailbox -AppId $AppClientID
##Test ApplicationAccessPolicy - Granted Scenario
Test-ApplicationAccessPolicy -Identity $AllowedMailbox -AppId $AppClientID
Upvotes: 4
Reputation: 2286
After long time discovering how to send an email from your server with OAuth2, I end up with this working example.
const msal = require('@azure/msal-node');
const fetch = require('node-fetch');
const clientSecret = process.env.CLIENT_SECRET;
const clientId = process.env.CLIENT_ID;
const tenantId = process.env.TENANT_ID;
const aadEndpoint =
process.env.AAD_ENDPOINT || 'https://login.microsoftonline.com';
const graphEndpoint =
process.env.GRAPH_ENDPOINT || 'https://graph.microsoft.com';
const msalConfig = {
auth: {
clientId,
clientSecret,
authority: aadEndpoint + '/' + tenantId,
},
};
const tokenRequest = {
scopes: [graphEndpoint + '/.default'],
};
const cca = new msal.ConfidentialClientApplication(msalConfig);
const tokenInfo = await cca.acquireTokenByClientCredential(tokenRequest);
const mail = {
subject: 'Microsoft Graph JavaScript Sample',
//This "from" is optional if you want to send from group email. For this you need to give permissions in that group to send emails from it.
from: {
emailAddress: {
address: '[email protected]',
},
},
toRecipients: [
{
emailAddress: {
address: '[email protected]',
},
},
],
body: {
content:
'<h1>MicrosoftGraph JavaScript Sample</h1>This is the email body',
contentType: 'html',
},
};
const headers = new fetch.Headers();
const bearer = `Bearer ${tokenInfo.accessToken}`;
headers.append('Authorization', bearer);
headers.append('Content-Type', 'application/json');
const options = {
method: 'POST',
headers,
body: JSON.stringify({ message: mail, saveToSentItems: false }),
};
await fetch(
graphEndpoint + '/v1.0/users/[email protected]/sendMail',
options
);
Also take a look here for email setting:
https://learn.microsoft.com/en-us/graph/api/user-sendmail?view=graph-rest-1.0&tabs=javascript
May be it will help someone ;)
Upvotes: 12
Reputation: 534
I'd suggest that you use Microsoft Graph to send emails. There's an easy to use REST API that works greatly with OAuth.
Please find below some links to help you build this out quickly.
https://learn.microsoft.com/en-us/graph/api/user-sendmail?view=graph-rest-1.0&tabs=http https://learn.microsoft.com/en-us/graph/auth/auth-concepts?view=graph-rest-1.0 https://learn.microsoft.com/en-us/graph/tutorials/node?view=graph-rest-1.0
Upvotes: 4