Sending Email from Node using the Microsoft Graph API

Written by James McDonald

January 30, 2022

First you need to create an Azure AD Application that has Application access to the Mail.Send role of the Microsoft Graph API. You can do this manually or programmatically using Powershell as per my previous

The next step is creating a sample node application that has the following dependencies. Contents on package.json:

{
  "name": "mailsend",
  "version": "1.0.0",
  "description": "Test of mail send through Microsoft Graph",
  "main": "mailSend.js",
  "scripts": {
    "start": "node mailSend.js",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "axios": "^0.25.0",
    "dotenv": "^14.3.2",
    "qs": "^6.10.3"
  }
}

Contents of .env (add this to your .gitignore don’t commit this to the public repo)

TENANT_ID=<your azure Tenant ID>
CLIENT_ID=<Application (client) ID>
CLIENT_SECRET=<Client secret>
AAD_ENDPOINT=https://login.microsoftonline.com
GRAPH_ENDPOINT=https://graph.microsoft.com
[email protected] 
# from is an address in the above azure tenant
SUBJECT=From James McDonald Subject

Contents on mailSend.js

// const fetch = require('node-fetch');
// pulls in the .env file settings
require("dotenv").config();

const axios = require("axios");
const qs = require("qs");

const TENANT_ID = process.env.TENANT_ID || "";
const CLIENT_ID = process.env.CLIENT_ID || "";
const CLIENT_SECRET = process.env.CLIENT_SECRET || "";
const AAD_ENDPOINT = process.env.AAD_ENDPOINT || "";
const GRAPH_ENDPOINT = process.env.GRAPH_ENDPOINT || "";
const from = process.env.FROM || "";
const subject = process.env.SUBJECT || "";

const recipientAddresses = {
  to: [{ address: "[email protected]", name: "A Test DisplayName" }],
  cc: [{ address: "[email protected]", name: "Another Test Display Name" }],
  bcc: [{ address: "[email protected]", name: "A Third Test Display Name" }],
};

function addRecipients(messageBody, rcpts = {}) {
  cloned = Object.assign({}, messageBody);

  Object.keys(rcpts).forEach((element) => {
    if (rcpts[element].length > 0) {
      cloned.message[element + "Recipients"] = createRecipients(rcpts[element]);
    }
  });

  return cloned;
}

function createRecipients(rcpts) {
  return rcpts.map((rcpt) => {
    return {
      emailAddress: {
        address: rcpt.address,
        name: rcpt.name || "",
      },
    };
  });
}

const createEmailAsJson = (rcpts, subject, body) => {
  let messageAsJson = {
    message: {
      subject: subject,
      body: {
        contentType: "HTML",
        content: body,
      },
    },
  };

  messageAsJson = addRecipients(messageAsJson, rcpts);

  return messageAsJson;
};

const getAuthToken = async () => {
  const formData = {
    grant_type: "client_credentials",
    scope: `${GRAPH_ENDPOINT}/.default`,
    client_id: CLIENT_ID,
    client_secret: CLIENT_SECRET,
  };

  console.log("url", `${AAD_ENDPOINT}/${TENANT_ID}/oauth2/v2.0/token`);

  const tokenObject = await axios({
    url: `${AAD_ENDPOINT}/${TENANT_ID}/oauth2/v2.0/token`,
    method: "POST",
    data: qs.stringify(formData),
    headers: {
      "Content-Type": "application/x-www-form-urlencoded",
    },
  });

  const {
    data: { access_token },
  } = tokenObject;

  return access_token;
};

const sendNotification = async (from, message) => {
  const access_token = await getAuthToken();
  try {
    const response = await axios({
      url: `${GRAPH_ENDPOINT}/v1.0/users/${from}/sendMail`,
      method: "POST",
      headers: {
        Authorization: `Bearer ${access_token}`,
        "Content-Type": "application/json",
      },
      data: JSON.stringify(message),
    });

    console.log("sendNotification status", response.statusText);
  } catch (error) {
    console.log(error);
  }
};

const body = "<h1>Test from James A</h1><p>HTML message sent via MS Graph</p>";

const message = createEmailAsJson(recipientAddresses, subject, body);
console.log(JSON.stringify(message, null, "  "));
sendNotification(from, message);

0 Comments

Submit a Comment

Your email address will not be published. Required fields are marked *

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

The reCAPTCHA verification period has expired. Please reload the page.

You May Also Like…

Clear HSTS Settings in CHrome

Open chrome://net-internals/#hsts enter the domain in the query field and click Query to confirm it has HSTS settings...

Ubuntu on Hyper-v

It boils town to installing linux-azure # as root or sudo apt-get update apt-get install linux-azure...