yash dogra
yash dogra

Reputation: 99

Modern Oauth2 authentication for sending mails using Nodemailer nodejs

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

Answers (3)

PotatoFarmer
PotatoFarmer

Reputation: 2994

SMTP is being limited (for Office365)


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].

Implementation: Send Email (via Microsoft Graph-> sendMail)


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


  1. Create an Azure App Registration

  2. 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!

  3. Grant Admin Consent to permission to your newly created Mail.Send permission (likely requires Global Admin)

  4. Create Application Password: Azure App Registration Admin > Certificates and secrets > Client Secrets -> New client secret (note the generated secret)

    • Create a reminder/plan to refresh this secret every expiration period (in Azure as well as your own code)

Appendix 2: Limit Mail.Send permission


  1. Office 365 Admin: Create a mail-enabled security group (note the group name)

  2. Add Members to that group: Add user(s) whose mailbox you want your App Registration to be able impersonate via Mail.Send.

  3. 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

Bogdan Le
Bogdan Le

Reputation: 2286

After long time discovering how to send an email from your server with OAuth2, I end up with this working example.

  1. Create an app https://go.microsoft.com/fwlink/?linkid=2083908
  2. Add permissions in {Your APP admin panel} > API permissions > Add permission > Microsoft Graph > Application permissions > Mail.Send > Add permissions
  3. Create certificate to get client secret {Your APP admin panel} > Certificates & secrets > Client secrets > New client secret (Save the "Value" string somewhere this is your client_secret)
  4. Make sure you have installed required node apps
  5. Now you can run this code to send any email from your server:
    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

Sivaprakash-MSFT
Sivaprakash-MSFT

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

Related Questions