Nodemailer - massive delay or missing emails in production - node.js

I currently have a site using Nodemailer & Gmail that works fine in my local development environment - any email sends instantly to the desired address.
Sadly, in production, only the admin notifications are being sent and the user ones are taking a very long time to deliver or not delivering at all. Ones that have arrived successfully took around 1 hour. The receiving email for admin emails is of the same domain as the URL of the website which makes me consider whether it's a domain verification issue. Only external recipients seem to get the delay.
My code is as follows:
const nodemailer = require('nodemailer')
const gmailUser = process.env.GMAIL_USER
const gmailPass = process.env.GMAIL_PASS
const appTitle = process.env.APP_TITLE
const receivingEmail = process.env.MAIL_RECEIVE
const smtpTransporter = nodemailer.createTransport({
host: 'smtp.gmail.com',
port: 465,
secure: true,
auth: {
user: gmailUser,
pass: gmailPass
}
})
const emailConfirmation = (userEmail) => {
const userMailOptions = {
from: appTitle,
to: userEmail,
subject: `${appTitle} - User Confirmation`
text: 'User Confirmation'
}
const adminMailOptions = {
from: appTitle,
to: receivingEmail,
subject: `${appTitle} - Admin Confirmation`,
text: 'Admin Confirmation'
}
Promise.all([
smtpTransporter.sendMail(userMailOptions),
smtpTransporter.sendMail(adminMailOptions)
])
.then((res) => { return true })
.catch((err) => { console.log("Failed to send email confirmations: ", err); return false })
}
I then call the function in a POST handler as follows:
emailConfirmation(user.email)
Am I doing something wrong in my code, or is this likely to be some sort of domain verification error?

I ended up deciding to switch to a different mail provider and have since had no issues.

Related

How to Resolve Error Sending Email Using nodemailer googleapi?

Last week I initially posted this asking for help using nodemailer and googleapi. I'm trying to use nodemailer and googleapis to send an email. I have set up my project in https://console.cloud.google.com/ and have set my CLIENT_ID, CLIENT_SECRET, CLIENT_REDIRECT_URI and REFRESH_TOKEN in a .env and have confirmed that the values are being populated. In debug mode I have noticed the following error stack when I send the error:
'Error: invalid_grant\n at Gaxios._request (/Users/ENV/Tutoring-Invoice-Management-System/node_modules/gaxios/build/src/gaxios.js:130:23)\n at processTicksAndRejections
(node:internal/process/task_queues:96:5)\n
at async OAuth2Client.refreshTokenNoCache (/Users/ENV/Tutoring-Invoice-Management-System/node_modules/google-auth-library/build/src/auth/oauth2client.js:174:21)\n
at async OAuth2Client.refreshAccessTokenAsync (/Users/ENV/Tutoring-Invoice-Management-System/node_modules/google-auth-library/build/src/auth/oauth2client.js:198:19)\n
at async OAuth2Client.getAccessTokenAsync (/Users/ENV/Tutoring-Invoice-Management-System/node_modules/google-auth-library/build/src/auth/oauth2client.js:227:23)\n
at async sendMail (/Users/ENV/Tutoring-Invoice-Management-System/service/send-email.js:17:29)'
The code is below. I have edited it based on an answer to the question already. My question now is, why am I getting the invalid_grant error? Based on the formal documentation I have set everything up correctly in https://console.cloud.google.com/apis/credentials/oauthclient. But perhaps there is an issue there?
const nodemailer = require('nodemailer');
const { google } = require('googleapis');
require('dotenv').config();
console.log("CLIENT_ID: " + process.env.CLIENT_ID);
console.log("CLIENT_SECRET: " + process.env.CLIENT_SECRET);
console.log("CLIENT_REDIRECT_URI: " + process.env.REDIRECT_URI);
console.log("REFRESH_TOKEN: " + process.env.REFRESH_TOKEN);
const oAuth2Client = new google.auth.OAuth2(process.env.CLIENT_ID, process.env.CLIENT_SECRET, process.env.REDIRECT_URI);
console.log("oAuth2Client: " + oAuth2Client);
oAuth2Client.setCredentials({refresh_token: process.env.REFRESH_TOKEN})
async function sendMail() {
try {
const accessToken = await oAuth2Client.getAccessToken()
const transport = nodemailer.createTransport({
host: "smtp.gmail.com",
port: 465,
secure: true,
auth: {
type: 'OAuth2'
}
});
const mailOptions = {
from: 'envolonakis#gmail.com',
to: 'envolonakis#gmail.com',
subject: "Test Email API Subject",
text: "Test Email API Text",
html: "<h1> Test Email API HTML </h1>",
auth: {
user: process.env.OWNER_EMAIL,
accessToken: accessToken.token
}
}
const result = await transport.sendMail(mailOptions);
return result;
} catch (error) {
console.log(error.stack);
return error;
}
}
sendMail()
From the official documentation, this is what you need to use:
try {
const accessToken = await oAuth2Client.getAccessToken()
const transport = nodemailer.createTransport({
host: "smtp.gmail.com",
port: 465,
secure: true,
auth: {
type: 'OAuth2'
}
});
const mailOptions = {
from: process.env.OWNER_EMAIL,
to: process.env.RECIPIENT,
subject: "Test Email API Subject",
text: "Test Email API Text",
html: "<h1> Test Email API HTML </h1>",
auth: {
user: process.env.OWNER_EMAIL,
accessToken: accessToken.token
}
}
const result = await transport.sendMail(mailOptions);
return result;
} catch (error) {
return error;
}
One error that you have whilst using the google api authentication library is with the token. You were passing the complete token object to the auth configuration of nodemailer instead of just the access token string. Another thing to keep in mind, adding or removing parameters to the auth configuration of nodemailer will lead to different errors.
Using #Morfinismo 's solution in addition to creating a new oAuth on https://developers.google.com/oauthplayground resolved my issue.

how to send email with nodejs from heroku

I have this nodejs app am running and am trying to send an email to newly register users like a verification link. On my local server it works well but when I deployed to heroku it always fails.
my nodejs code
var transporter = nodemailer.createTransport({
host: 'smtp.gmail.com',
port: 465,
auth: {
user: 'gmail.com',
pass: 'password'
}
});
var mailOptions = {
from: 'gmail#gmail.com',
to: email,
subject: 'Verification code',
html: `<h1 style="color:blue,font-weight:bold,text-transform-uppercase"></h1></p>
<p style="color:black,font-weight:bold,text-align:center, font-size:20px">${pin}</p>
<span>this verification process helps comfirm that your the real owner of this account, so we can
help protect you from scams</span>
<p>click the link ${v_address} to go to the verification page</p>`
};
transporter.sendMail(mailOptions, async function(error, info){
if (error) {
res.json({error:'failed please check details or network connection'});
}else {
const newCrete = await Create.save()
if(newCrete){
const newVerify = new Verifyuser({
email:email,
pin:pin,
address:v_address
})
const Verified = await newVerify.save()
res.json({success:'success'})
}
}
});
Kindly use the NPM Package (two-step-auth)
This will take care of the whole verification process, you don't need to worry about the backend work :), This will give you an OTP and the client email an OTP and you can check if they match and you can verify the Email ID,
Kindly check the full procedures with example here
Usage
const {Auth} = require('two-step-auth');
async function login(emailId){
const res = await Auth(emailId);
// You can follw the above approach, But we recommend you to follow the one below, as the mails will be treated as important
const res = await Auth(emailId, "Company Name");
console.log(res);
console.log(res.mail);
console.log(res.OTP);
console.log(res.success);
}
login("YourEmail#anyDomain.com")
Output
This will help you a lot taking care of the process of verification under the HOOD :)

Add various fields in the text section of nodemailer

I'm sending mail from my angular app using nodemailer. I have a variable which has several attributes like firstname, middlename, email, mobile, address. I'm fetching this data from firebase and each variable can be accessed by writing $data.firstname, $data.email. I was able to send only 1 variable right now. I want to send all the variables to the mail with labels
So mail content should be
Email - abc#123
First Name - ABC
Address - LMN
Mobile - 7777777777
Please help me out.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const nodemailer = require('nodemailer');
admin.initializeApp()
require('dotenv').config()
const {SENDER_EMAIL, SENDER_PASS} = process.env;
exports.sendMailNotification1=functions.firestore.document('submissions/{docID}')
.onCreate((snap, ctx)=> {
const data=snap.data();
let authData=nodemailer.createTransport({
host: 'smtp.gmail.com',
port: 465,
secure:true,
auth:{
user: SENDER_EMAIL,
pass: SENDER_PASS
}
});
authData.sendMail({
from: 'xxx#gmail.com',
to: 'xyz#gmail.com',
subject: 'Appointment Info ',
text:`${data.fname}`,
html:`${data.email}`,
}).then(res=>console.log('Succesfully Sent')).catch(err=> console.log(err)
);
})
As explained in the NodeMailer doc, you can choose between:
using the message's text element for sending "the plaintext version of the message"
OR
using the message's html element for sending "the HTML version of the message".
So for example, if you use the HTML option you could use an HTML list as follows:
//...
const htmlContent = `<ul><li>Email - ${data.email}</li><li>Address - ${data.address}</li></ul>`;
return authData.sendMail({
from: 'xxx#gmail.com',
to: 'xyz#gmail.com',
subject: 'Appointment Info',
html: htmlContent
})
.then(res => {
console.log('Succesfully Sent');
return null;
})
.catch(err => {
console.log(err);
return null;
});
Note the addition of several returns in the code, see https://firebase.google.com/docs/functions/terminate-functions for more info on this key aspect.

Emails sent via nodemailer from my server come through as spam: (unknown sender)

First time using Node and working with email configuration. I downloaded this application from here, it works (uses mustache.js to generate templates for the emails), but the test email ends up in my gmail spam.
from: via vps1234.inmotionhosting.com
to: thisisme#gmail.com
date: Tue, Aug 8, 2017 at 5:30 PM
subject: Thanks! and review your experience
mailed-by: vps1234.inmotionhosting.com
security: Standard encryption (TLS) Learn more
-
var nodemailer = require('nodemailer');
let transporter = nodemailer.createTransport({
service: ' ',
secure: false,
port: 25,
auth: {
user: 'thisisme#mydomain.com',
pass: 'password1234'
},
tls: {
rejectUnauthorized: false,
}
}),
EmailTemplate = require('email-templates').EmailTemplate,
path = require('path'),
Promise = require('bluebird');
let users = [
{
name: 'John',
note: 'I found your purse',
email: 'recipient#gmail.com',
}
];
function sendEmail (obj) {
return transporter.sendMail(obj);
}
function loadTemplate (templateName, contexts) {
let template = new EmailTemplate(path.join(__dirname, 'templates', templateName));
return Promise.all(contexts.map((context) => {
return new Promise((resolve, reject) => {
template.render(context, (err, result) => {
if (err) reject(err);
else resolve({
email: result,
context,
});
});
});
}));
}
loadTemplate('welcome', users).then((results) => {
return Promise.all(results.map((result) => {
sendEmail({
to: result.context.email,
from: 'Me :)',
subject: result.email.subject,
html: result.email.html,
text: result.email.text,
});
}));
}).then(() => {
console.log('Yay!');
});
This is Nodemailer boilerplate, which I tested also on my server, and it worked correctly, and the email did not get flagged:
var nodemailer = require('nodemailer');
let transporter = nodemailer.createTransport({
service: ' ',
secure: false,
port: 25,
auth:{
user: 'thisisme#mydomain.com',
pass: 'password1234'
},
tls: {
rejectUnauthorized: false,
}
});
let helperOptions = {
from: '<thisisme#mydomain.com>',
to: 'recipient1234#gmail.com',
};
transporter.sendMail(helperOptions, (error, info) =>{
if(error){return alert(error);}
console.log("sent" . info);
})
A message being marked as spam is not so much a function of the code that generated it but of a few other things such as:
the actual content (text, links etc)
the domain it came from
The first issue is just something you have to play with. There are services such as MailMonitor and others that help you tweak your content to see how gmail and others handle it. Wording, HTML vs plain text, links vs none, etc all play a part.
As far as the domain, you'll want to setup your SPF (Sender Policy Framework) and DKIM entries to essentially validate your domain as a proper sender. The "unknown sender" is most likely a missing SPF record.
For SPF here's an article.
For DKIM here's another
Note that I just googled for this - they seemed like good articles but I am sure there are other great sources.
The gist of it is that you'll want to create a couple of TXT entries in your DNS. There are tools such as SPF generators to help you with this. It's not as complex as it may sound.
Once that is done you might still end up in spam but it will certainly help. You can experiment with services such as Amazon SES and SendGrid which might provide better sending "validity" than your current SMTP server.

Sending emails using Mailgun with NodeMailer package

A couple of days ago I realized that Google has changed the security of gmail accounts, particularly for the possibility of sending emails from applications. After Googling around for a while I couldn't find a fix for it.
So, I resorted to using Mailgun. I created an account and had it enabled with Business verification. However, I still can't send emails. I keep getting an error about the requested URL not being found.
I am suspecting that since I haven't set up a domain yet, it is not picking the mailgun domain it provided by default. Could someone show me how to test sending emails using Mailgun from NodeMailer indicating the sandbox name provided by mailgun.
thanks in advance
José
var nodemailer = require('nodemailer');
// send mail with password confirmation
var transporter = nodemailer.createTransport( {
service: 'Mailgun',
auth: {
user: 'postmaster#sandboxXXXXXXXXXXXXXXXX.mailgun.org',
pass: 'XXXXXXXXXXXXXXXX'
}
});
var mailOpts = {
from: 'office#yourdomain.com',
to: 'user#gmail.com',
subject: 'test subject',
text : 'test message form mailgun',
html : '<b>test message form mailgun</b>'
};
transporter.sendMail(mailOpts, function (err, response) {
if (err) {
//ret.message = "Mail error.";
} else {
//ret.message = "Mail send.";
}
});
I created the Nodemailer transport for mailgun.
Here it how it works.
You install the package with npm install as you would do with any package, then in an empty file:
var nodemailer = require('nodemailer');
var mg = require('nodemailer-mailgun-transport');
// This is your API key that you retrieve from www.mailgun.com/cp (free up to 10K monthly emails)
var auth = {
auth: {
api_key: 'key-1234123412341234',
domain: 'sandbox3249234.mailgun.org'
}
}
var nodemailerMailgun = nodemailer.createTransport(mg(auth));
nodemailerMailgun.sendMail({
from: 'myemail#example.com',
to: 'recipient#domain.com', // An array if you have multiple recipients.
subject: 'Hey you, awesome!',
text: 'Mailgun rocks, pow pow!',
}, function (err, info) {
if (err) {
console.log('Error: ' + err);
}
else {
console.log('Response: ' + info);
}
});
Replace your API key with yours and change the details and you're ready to go!
It worked me, when I added the domain also to the auth object (not only the api_key). Like this:
var auth = {
auth: {
api_key: 'key-12319312391',
domain: 'sandbox3249234.mailgun.org'
}
};

Resources