DEV Community

Cover image for Sending Emails Securely Using Node.js, Nodemailer, SMTP, Gmail, and OAuth2

Sending Emails Securely Using Node.js, Nodemailer, SMTP, Gmail, and OAuth2

Chandra Panta Chhetri on December 16, 2020

Many solutions online regarding configuring Nodemailer to use your Gmail requires you to enable less secure app access. If that sounds too scary fo...
Collapse
 
kotwani2883 profile image
Palak Kotwani

I am getting this error . UnhandledPromiseRejectionWarning: Failed to create access token :(
(Use node --trace-warnings ... to show where the warning was created)
(node:7900) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag --unhandled-rejections=strict (see nodejs.org/api/cli.html#cli_unhand...). (rejection id: 1)
(node:7900) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise

Collapse
 
nokha_debbarma profile image
Nokha Debbarma

As sendEMail is a async function try add try-catch block inside sendEmail function.

Collapse
 
typicoul profile image
Luteya Coulston

I'm getting the same, did you find a solution?

Collapse
 
nokha_debbarma profile image
Nokha Debbarma • Edited

As sendEMail is a async function try adding try-catch block inside sendEmail function.

Thread Thread
 
sailee14 profile image
sailee14

After adding try catch block getting "Error: No refresh token or refresh handler callback is set." this error. Any way to make it right?

Collapse
 
bytecodeman profile image
Tony Silvestri • Edited

Hi
I'm consistently getting this error:
"Failed to create access token: Error: invalid_grant" after having an app work fine for a week or so. After this period this error pops up.

I go to OAuth playground to regenerate the refreshToken. App works fine only to fail sometime later.

I pretty much followed (heck copied!! :-)) the code offered in this article.

Any ideas? Much appreciation in advance!

Collapse
 
abeertech01 profile image
Abdul Ahad Abeer • Edited

Add the email you are using to send email here in OAuth consent screen page as a test user

Collapse
 
bytecodeman profile image
Tony Silvestri

Hi. It's there already. But Thx for replying!!!

Thread Thread
 
abeertech01 profile image
Abdul Ahad Abeer

I will write a blog on it. once writing is completed, I will leave a link here

Thread Thread
 
bytecodeman profile image
Tony Silvestri

Thank you for your efforts! They are much appreciated.
Tony

Collapse
 
poziminski profile image
Pawel Oz

I actually dont understand what happens next, in production. I guess oauthplayground does not stay there and we need to implement our authorization endpoint (redirect_uri) which is not much covered in the article. I think we should not use oauthplayground refresh token in production?

Collapse
 
springboot20 profile image
springboot20

please am having a problem with sending dynamic email using nodemailer express handlebars here is my code
import nodemailer from "nodemailer"
import {
google
} from "googleapis"
import fs from "fs"
import path from "path"
import url from "url"
import expressHandlerbars from "nodemailer-express-handlebars"

const filename = url.fileURLToPath(import.meta.url)
const __dirname = path.dirname(
filename)

const OAuth2 = google.auth.OAuth2
const createTransporter = async () => {
const OAuth2Client = new OAuth2(
process.env.CLIENT_ID,
process.env.CLIENT_SECRET,
"developer.google.com/oauthplayground"
)

OAuth2Client.setCredentials({
refresh_token: process.env.REFRESH_TOKEN
})

const accessToken = await new Promise((resolve, reject)=> {
OAuth2Client.getAccessToken((error, token => {
if (error) {
console.log(error)
reject("Failed to get accessToken")
}
resolve(token)
}))
})

return await nodemailer.createTransport({
service: "gmail",
auth: {
type: "OAuth2",
accessToken,
user: process.env.EMAIL,
refreshToken: process.env.REFRESH_TOKEN,
clientId: process.env.CLIENT_ID,
clientSecret: process.env.CLIENT_SECRET,
}, tsl: {
rejectUnauthorized: false
}
})
}

const sendMail = async (email, subject, payload, template)=> {
const options = {
from: process.env.EMAIL,
to: email,
subject:subject,
template: template,
context: payload,
}

try {
const transporter = await createTransporter()

transporter.use("compile", expressHandlebars({
  viewEngine: {
    extName: '.hbs',
    partialsDir: 'views/partials',
    layoutsDir: 'views/layouts',
    defaultLayout: 'layout',
  },
  extName: '.hbs',
  viewPath: 'views',
}))

await transporter.sendMail(options) 
Enter fullscreen mode Exit fullscreen mode

} catch(error) {
console.log(${error})
}
}

export default sendMail
so I am receiving a reference error how can I fix it and do it in the right way

Collapse
 
nuzumpat profile image
Pat Nuzum

I am getting the following error:
Error: Invalid login: 535-5.7.8 Username and Password not accepted.

It works correct when I run your program in a standalone mode, but when I am your logic to my project, I get the above error.

Collapse
 
nandhamurali profile image
Nandhakumar

Were you able to fix the issue?

Collapse
 
jasonwarner profile image
Jason

Thank you so much, Chandra!

I was able to follow your directions for my Next.js application and got it to work!

This OAUTH stuff is definitely not intuitive, though.

For anyone struggling with implementing this, here is the GitHub link for my little project: github.com/jason-warner/hustlin-la...

And a few tips:

  • be extra careful to wrap your requests in try/catch
  • do not append your ENV variables with a semicolon ( ; )
  • use console logs to confirm every single one of your assumptions (you have access to environment variables, you have a an access token before you try to send mail, etc. and log any possible errors)
Collapse
 
benmneb profile image
benmneb

This is much easier to do now! stackoverflow.com/a/45479968/12104850

Collapse
 
smozam profile image
SMoZam

Hey,
Thanks for this post.
Refresh token expires in one week for me is it normal ?
Thanks

Collapse
 
lefis profile image
lefterispour

When I'm clicking to authorize API's and selecting my google account I'm getting this error: "appname has not completed the Google verification process. The app is currently being tested, and can only be accessed by developer-approved testers. If you think you should have access, contact the developer.
If you are a developer of appname, see error details.
Error 403: access_denied.

Collapse
 
shmoji profile image
Joshua T Jackson

What's the point of using Nodemailer when you can send emails using just the Gmail API?

Collapse
 
alohe profile image
Alohe

Its easier to implement i guess

Collapse
 
yourispen profile image
Yourispen TANKO MBA ESSONO

Hi. I hope you're doing well. I used your method and everything works correctly, but only locally. In production (on Vercel for example), emails never go out. I do not understand. Can you help me please?

Collapse
 
andyoooh profile image
AndyOoh

Good article but 'ridiculously easy though'? :/

Can anyone mention some simpler alternatives?

Collapse
 
nokha_debbarma profile image
Nokha Debbarma

issue!

google always override from:

with authenticated email address even though I have provided with other email address with gmail domain.

could you please help me out?
Collapse
 
aeden profile image
Frédéric HAUGUEL

Too difficult, just to send an email

Collapse
 
rguktong profile image
rguktong

it is showing sendMail is not defined

Collapse
 
medeirosdev profile image
Rafael Medeiros

Thanks for the post bro, it helped me a lot

Collapse
 
atelypham profile image
Trung-Tin Pham • Edited

This is an awesome blog. Learn a lots <3

Collapse
 
raulbarriga profile image
raulbarriga

So if I want to use nodemailer for production deployment, the googleapis package will allow for the token to never expire?

Collapse
 
alx4329 profile image
Alexis Martinez

Great tutorial! Thanks for sharing
Worked at first try

Collapse
 
rajko_podinic profile image
Rajko Podinic

How would you implement this via a NextJS application and use a contact form submission to send the details to a Gmail email?