How to Resolve Error Sending Email Using nodemailer googleapi? - node.js

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.

Related

Nodemailer - massive delay or missing emails in production

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.

how to send gmail using nodemailer (solution of less secure app deprecated)

I am trying to send mail using nodemailer node module and configured like this.
... config.js ...
smtp: {
host: "smtp.gmail.com",
port: 465,
secure: true,
auth: {
user: "***#gmail.com",
pass: "****"
}
}
... mail.js ...
const config = require("../config");
const nodemailer = require('nodemailer');
const transporter = nodemailer.createTransport(config.smtp);
const sendEmail = async (data, res) => {
// if(!data.to || !data.subject)
// return false;
data = { ...data, from: config.smtp.auth.user };
console.log(data)
transporter.sendMail(data, (error, info) => {
console.log(error);
res.send(error);
});
res.send(true)
}
module.exports = sendEmail;
... controller ...
router.get('/testEmail', async (req, res) => {
let emailData = {
to: "***#gmail.com",
subject: "Welcome to Musical World",
text: "test test"
};
let result = sendEmail(emailData, res);
console.log(result)
// res.send(result);
})
When I try to send email, it returns
{"code":"EAUTH","response":"535-5.7.8 Username and Password not accepted. Learn more at\n535 5.7.8 https://support.google.com/mail/?p=BadCredentials p25-20020a056638217900b0033c14d2386bsm898629jak.75 - gsmtp","responseCode":535,"command":"AUTH PLAIN"}
So I did googling and found that's why I didn't turn on less secure app in my google account.
But when I went there, google said
I tried to turn on less secure apps , but Gless secure apps feature isn't available.
What do I have to do , then?
I finally resolved by myself!.
Google no longer supports less secure app feature from 05/20/2022.
It will help other developers who get stuck with this issue.
So we can't access to gmail using only emal and password.
Instead we have to use OAuth2 to get access to gmail.
... config.js ...
smtp: {
service: "gmail",
auth: {
type: "OAuth2",
user: "*****#gmail.com",
pass: "******",
clientId: "********",
clientSecret: "***********",
refreshToken: "************"
}
}
You have to have OAuth2 client in your google cloud console for this.
Here's full details you can refer to.
https://www.freecodecamp.org/news/use-nodemailer-to-send-emails-from-your-node-js-server/

Google OAuth2.0 - Gaxios invalid_grant error

The below is my mailer.js code
const nodemailer = require("nodemailer");
const { google } = require("googleapis");
const { OAuth2 } = google.auth;
const oauth_link = "https://developers.google.com/oauthplayground";
const { EMAIL, MAILING_ID, MAILING_REFRESH, MAILING_SECRET } = process.env;
const auth = new OAuth2(
MAILING_ID,
MAILING_SECRET,
MAILING_REFRESH,
oauth_link
);
exports.sendVerificationEmail = (email, name, url) => {
console.log("inside send verification email");
auth.setCredentials({
refresh_token: MAILING_REFRESH,
});
const accessToken = auth.getAccessToken();
console.log("accessToken-" + accessToken);
const smtp = nodemailer.createTransport({
service: "gmail",
auth: {
type: "OAuth2",
user: EMAIL,
clientId: MAILING_ID,
clientSecret: MAILING_SECRET,
refreshToken: MAILING_REFRESH,
accessToken,
},
});
console.log("smtp " + smtp);
const mailOptions = {
from: EMAIL,
to: email,
subject: "Facebook email verification",
html: '<div><div style="max-width:500px;margin-bottom:1rem;align-items:center;display:flex"><img style="width:40px" src="https://seeklogo.com/images/F/facebook-icon-circle-logo-09F32F61FF-seeklogo.com.png" alt=""><span style="font-family:sans-serif;color:#3266a8;font-weight:700;padding-left:10px">Action Required : Activate your facebook account</span></div><div style="border-top:1px solid;border-bottom:1px solid;border-color:#deb887;padding:10px;padding-bottom:20px"><div style="height:35px"><span style="font-weight:700;font-family:sans-serif">Hello ${name}</span></div><div style="height:28px;font-family:sans-serif;padding-bottom:20px"><span>You recently created a profile on facebook. Please confirm your account</span></div><div style="">Confirm Your Account</div></div></div>',
};
console.log("mailOptions" + mailOptions);
smtp.sendMail(mailOptions, (err, res) => {
if (err) return err;
return res;
});
};
I have properly generated the Oauth playground configurations and have the below in my process.env
EMAIL=***
MAILING_ID=***
MAILING_SECRET=***
MAILING_REFRESH=***
MAILING_ACCESS=***
I am getting the below error.
GaxiosError: invalid_grant
***
***
data: {
error: 'invalid_grant',
error_description: 'Token has been expired or revoked.'
},
I am totally beginner with the Google OAuth procedure. Which token has expired? Is it Access token or Refresh Token.
Thanks in advance, Dear Developers community..
The refresh token you are using has expired. You need to request a new one. If your project is still in testing refresh tokens only last for seven days.
You need to set it to production in Google cloud console under the OAuth screen and your refresh tokens will no longer expire
I applaud your use of xOAuth2 with the smtp server 👏

Error: connect ECONNREFUSED 127.0.0.1:587 – nodemailer & gmail

Dear stack overflow community,
I am dealing with a project and I tried many things, but I can't handle it.
I created a website with NextJS. This Website has a contact form. To send the data from the contact form to the addressee, there is an API which takes the request and with nodemailer I am trying to create a transporter to send the email with Gmail.
const nodemailer = require("nodemailer");
const { google } = require("googleapis");
export default async function (req, res) {
const body = JSON.parse(req.body);
const CLIENT_EMAIL = process.env.CLIENT_EMAIL;
const CLIENT_ID = process.env.CLIENT_ID;
const CLIENT_SECRET = process.env.CLIENT_SECRET;
const REDIRECT_URI = process.env.REDIRECT_URI;
const REFRESH_TOKEN = process.env.REFRESH_TOKEN;
const OAuth2Client = new google.auth.OAuth2(
CLIENT_ID,
CLIENT_SECRET,
REDIRECT_URI
);
OAuth2Client.setCredentials({ refresh_token: REFRESH_TOKEN });
try {
// Generate the accessToken on the fly
const accessToken = await OAuth2Client.getAccessToken();
// Account authentication config
const authConfig = {
type: "OAuth2",
user: CLIENT_EMAIL,
clientId: CLIENT_ID,
clientSecret: CLIENT_SECRET,
refreshToken: REFRESH_TOKEN,
accessToken: accessToken,
};
// Create the email envelope (transport)
const transport = nodemailer.createTransport(authConfig);
// Create the email options and body
// ('email': user's email and 'name': is the e-book the user wants to receive)
const mailOptions = {
from: `${CLIENT_EMAIL}`,
to: body.email,
subject: `${body.name} – ${body.reason}`,
text: `${body.message}`,
};
const result = await transport.sendMail(mailOptions);
return result;
} catch (error) {
console.log(error);
}
res.status(200).json({
message: "Email has been sent",
});
}
The problem is, when the User sends the email, I get the following error message:
Error: connect ECONNREFUSED 127.0.0.1:587
at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1161:16) {
errno: -61,
code: 'ESOCKET',
syscall: 'connect',
address: '127.0.0.1',
port: 587,
command: 'CONN'
}
I tried a couple of things now, but I really do not make any progress at this point. I would be super happy if somebody could help me.
Thanks a lot in advance.
Just make http://localhost:8000 to http://127.0.0.1:8000. It works. Windows are not configured to figure this out. An IP address should be in the conventions of numbers.
http://127.0.0.1:8000
The above address is the default one for any computer

Node Mailer Error

I am using nodemailer in my Firebase Cloud Functions to send a mail when a data is added to the realtime database.
Code:
const functions = require('firebase-functions');
const nodemailer = require('nodemailer');
const gmailEmail = 'myemail.in#gmail.com';
const gmailPassword = 'mypassword';
const mailTransport = nodemailer.createTransport({
service: 'gmail',
auth: {
user: gmailEmail,
password: gmailPassword
}
});
const APP_NAME = 'ABC In'
exports.salonCreatedAccount = functions.database.instance('abc-
in').ref('/abc/{def}').onCreate(event => {
const snapshot = event.data;
const val = snapshot.val()
console.log(val);
const email = val.email;
const displayname = val.name;
return sendconfirmationEmail(email, displayname);
});
function sendconfirmationEmail(email, displayName){
const mailOptions = {
from: `${APP_NAME} <abc.in#gmail.com>`,
to: email
};
mailOptions.subject = `Welcome to ${APP_NAME}!`;
mailOptions.text = `Some Text`;
return mailTransport.sendMail(mailOptions).then(() => {
console.log(`New welcome mail sent to ${email}`);
});
}
I am getting this following error while executing.
NOTE: I have made sure that the email and password is right and there's no mistake there.
Error:
Error: Missing credentials for "PLAIN"
at SMTPConnection._formatError (/user_code/node_modules/nodemailer/lib/smtp-connection/index.js:591:19)
at SMTPConnection.login (/user_code/node_modules/nodemailer/lib/smtp-connection/index.js:340:38)
at connection.connect (/user_code/node_modules/nodemailer/lib/smtp-transport/index.js:270:32)
at SMTPConnection.once (/user_code/node_modules/nodemailer/lib/smtp-connection/index.js:188:17)
at SMTPConnection.g (events.js:292:16)
at emitNone (events.js:86:13)
at SMTPConnection.emit (events.js:185:7)
at SMTPConnection._actionEHLO (/user_code/node_modules/nodemailer/lib/smtp-connection/index.js:1113:14)
at SMTPConnection._processResponse (/user_code/node_modules/nodemailer/lib/smtp-connection/index.js:747:20)
at SMTPConnection._onData (/user_code/node_modules/nodemailer/lib/smtp-connection/index.js:543:14)
How do I fix this?
Rename password to pass in auth object. for e.g
const mailTransport = nodemailer.createTransport({
service: 'gmail',
auth: {
user: gmailEmail,
pass: gmailPassword
}
});
I had the same problem, and solved it like that.
found out by diving in the code of nodemailer where this message is being logged the code look like this.
if (this._auth.user && this._auth.pass) { // this condition <---
this._auth.credentials = {
user: this._auth.user,
pass: this._auth.pass
};
} else {
return callback(this._formatError('Missing credentials for "' + this._authMethod + '"', 'EAUTH', false, 'API'));
}
Hope this helps :)
Solved same error from a local nodejs test app, by what #Zaheen replied to: Nodemailer with Gmail and NodeJS
, part of his reply is copied here:
var smtpTransport = require('nodemailer-smtp-transport');
var transporter = nodemailer.createTransport(smtpTransport({
service: 'gmail',
host: 'smtp.gmail.com',
auth: {
user: 'somerealemail#gmail.com',
pass: 'realpasswordforaboveaccount'
}
}));
(BTW Accounts with 2FactorAuth cannot enable less secure apps).
I used nodemailer-smpt-transport, xoauth2, and added host to auth{}.
Haven't yet done it with Firebase, that's next ... hopefully.. :)
Using nodemailer, you have to providean email and password of the account you are sending the email from, Firebase stores that for you so you don't have to write it directly on your code.
For that to happen you have to configure the gmail.email and gmail.password Google Cloud environment variables.
In your console, within the path you store your functions /yourProject/functions$ set your variables this way:
firebase functions:config:set gmail.email="senderEmail#gmail.com" gmail.password="yourPassword"
and then you can use it this way:
const gmailEmail = functions.config().gmail.email;
const gmailPassword = functions.config().gmail.password;
const mailTransport = nodemailer.createTransport({
service: 'gmail',
auth: {
user: gmailEmail,
pass: gmailPassword
}
});
I hope this is helpful for you all , it what was causing the 'missing credentialas plain error' in my case
According to the official docs, GMail may disable basic auth in some special cases.
To prevent having login issues you should either use OAuth2 or use another delivery provider and preferably a dedicated one.
Read more here: https://nodemailer.com/usage/using-gmail/
I had this same error and could not find any solution online, until now.
My solution for this exact error "Missing credentials for "PLAIN"":
Go to Authentication in Firebase Console
Click on Sign-In Method
Select Google
Make sure it is enabled
That solved my error. (Same code as you)

Resources