Implement a mailing solution in Express nodejs - node.js

I'm creating an API that handles account verification via email, password recovery and possibly other things.
I want to create one place to send emails across my entire API.
i'm using nodeMailer.
My current setup is i'm a calling a method on the user model that send EmailVerification email.
I want to create a template outside the User model that could send either password recovery, email verification or other things.. Depending on the params that i pass to the function.
My user model:
userSchema.methods.generateEmailVerificationToken = function() {
const token = jwt.sign({_id: this._id, role: this.role},
config.get('jwtPrivateKey'));
return token;
};
userSchema.methods.generateAuthToken = function() {
const token = jwt.sign({_id: this._id, role: this.role},
config.get('jwtPrivateKey'));
return token;
};
userSchema.methods.sendEmailVerification = function (user) {
sendMail(user);
};
and this is my sendMail function:
const nodemailer = require('nodemailer');
module.exports = function sendMail (user) {
const transporter = nodemailer.createTransport({
service: 'gmail',
auth: {
user: 'mymail#gmail.com',
pass: 'mypass'
}
});
const mailOptions = {
from: 'mymail#gmail.com',
to: user.email,
subject: 'Email verification.',
html: `Please click this link to verify your email`
};
transporter.sendMail(mailOptions, function(error, info){
if (error) {
console.log(error);
} else {
console.log('Email sent: ' + info.response);
}
});
}
Thank you

Related

Why is my password reset link logic showing undefined in my Express app?

I am trying to create a route that sends someone a JWT token reset link. The Route that I am having issues with is as follows:
// REQUIREMENTS
require("dotenv").config();
const express = require("express"),
expressSanitizer= require("express-sanitizer"),
router = express.Router(),
passport = require("passport"),
User = require("../models/user"),
middleware = require("../middleware"),
mailchimp = require('#mailchimp/mailchimp_marketing'),
request = require('request'),
https = require('https'),
nodemailer = require("nodemailer"),
jwt = require('jsonwebtoken');
const JWT_SECRET = "jwt_secret";
router.post('/forgot-password', (req, res, next) =>{
let userEmail = req.body.email;
let link;
User.findOne({'email': req.query.email}, function(err, username){
let userEmail = req.body.email;
if(err) {
console.log(err);
}
var message;
if(username) {
console.log(username);
message = "email exists";
console.log(message);
// SEND TOKEN HERE
const secret = JWT_SECRET + username.password;
const payload = {
id: username.id,
email: userEmail
};
let token = jwt.sign(payload, secret, {expiresIn: '15m'});
// CHECK URL PARAMS
let link = process.env.URL`/${username.id}/${token}`;
console.log(link);
console.log(payload);
return link, payload;
}
});
let transporter = nodemailer.createTransport({
host: 'smtp.gmail.com',
port: 465,
secure: true,
auth: {
type: 'OAuth2',
clientId: authClient,
clientSecret: authCSecret
}
});
// Email Data
let mailOptions = {
from: *****, // sender address
to: userEmail, // list of receivers
replyTo: *****,
subject: 'Reset Your Password', // Subject line
html: '<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet"/><link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap"rel="stylesheet"/><link href="https://cdnjs.cloudflare.com/ajax/libs/mdb-ui-kit/6.0.1/mdb.min.css" rel="stylesheet"/><p class="lead">Hey, it looks like you forgot your password.</p><p class="card-text">Follow the link below to reset your password.<div class="text-center"><a class="btn btn-lg btn-primary my-3" href="'+link+'" target="_blank">click here</a></div></p><script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/mdb-ui-kit/6.0.1/mdb.min.js"></script>', // html body
auth: {
user: authUser,
refreshToken: authRefreshToken,
accessToken: authAccessToken,
expires: 3599
}
};
// send mail with defined transport object
transporter.sendMail(mailOptions, (error, info) => {
if (error) {
return console.log(error);
}
console.log('Message sent: %s', info.messageId);
console.log('Preview URL: %s', nodemailer.getTestMessageUrl(info));
res.send("A link has been sent to your email.");
});
});
I have confirmed the Nodemailer part works. I am just at a loss why the link is always showing as undefined when in my console it prints out a valid link to follow. All help would be appreciated!

Nodemailer error: verification email doesn't works

I am writing this code to sign up the user.
I used Nodemailer to send a verification email to activate the account.
The post request works well but I did not receive a verification email and here is my code:
const transporter = nodemailer.createTransport({
service: "Gmail",
auth: {
user: process.env.EMAIL_USERNAME,
pass: process.env.EMAIL_PASSWORD,
},
});
exports.signup = async(req, res) => {
const { email } = req.body
if (!email) {
return res.status(422).send({ message: "Missing email." })
}
try {
const existingUser = await User.findOne({ email }).exec();
if (existingUser) {
return res.status(409).send({
message: "Email is already in use."
});
}
const user = await new User({
_id: new mongoose.Types.ObjectId,
email: email
}).save();
const verificationToken = user.generateVerificationToken();
const url = `http://localhost:5000/api/verify/${verificationToken}`
transporter.sendMail({
to: email,
subject: 'Verify Account',
html: `Click <a href = '${url}'>here</a> to confirm your email.`
})
return res.status(201).send({
message: `Sent a verification email to ${email}`
});
} catch (err) {
return res.status(500).send(err);
}
}
WHEN I SEND THE REQUEST, SHOWS ME LIKE THIS
Go to Google account, than Security and Less secure app access, and set to ON. Restart Your app and Now should up and running ;-)
Note That - In Your .env file, enter Your credentials without any quotes

How to make nodemailer reusable in multiple module?

I have implemented nodemailer after the user registration, in the following way:
let transporter = nodemailer.createTransport({
service: 'gmail',
auth: {
user: process.env.EMAIL_USERNAME,
pass: process.env.EMAIL_PASSWORD
}
});
let mailOptions = {
from: process.env.EMAIL_USERNAME,
to: user.email,
subject: 'Verify your account',
text: 'Click here for verify your account'
};
transporter.sendMail(mailOptions, (error, info) => {
if (error) {
return console.log(error);
}
});
I don't like much this code because if I need to send an email in another module, I need to rewrite all the stuff above.
Since I'm new to NodeJS, I would like to know if I can remove this code redundancy make something like a utility or maybe an helper class. The goal is import the wrapper class and call a simple function to send the email.
Which is the best way to handle that?
I refactored your code to look like below and then save it as mail.js
let transporter = nodemailer.createTransport({
service: 'gmail',
auth: {
user: process.env.EMAIL_USERNAME,
pass: process.env.EMAIL_PASSWORD
}
});
let sendMail = (mailOptions)=>{
transporter.sendMail(mailOptions, (error, info) => {
if (error) {
return console.log(error);
}
});
};
module.exports = sendMail;
in Your other modules, say activation.js
var mailer = require('./mail.js');
mailer({
from: process.env.EMAIL_USERNAME,
to: user.email,
subject: 'Verify your account',
text: 'Click here for verify your account'
};);
you can use module.exports as follow :
create common service mail.js and write your mail sent code here
mails.js
module.exports = function (){
// mail sent code
}
require mail.js where you write mail sent code in other service and call mail sent function
otherService.js
var mail = require('mail.js') // require mail sent in other service where you want to send mail
mail.sent() // call function of mail.js
I have created a class for this:
import NodeMailer from 'nodemailer'
import emailConfig from '../../config/mail' // read email credentials from your config
class EmailSender {
transport
constructor() {
this.transport = NodeMailer.createTransport({
host: emailConfig.MAIL_HOST,
port: emailConfig.MAIL_PORT,
auth: {
user: emailConfig.MAIL_USERNAME,
pass: emailConfig.MAIL_PASSWORD,
},
})
}
async sendMessage(to, subject, text, html) {
let mailOptions = {
from: emailConfig.MAIL_FROM_ADDRESS,
to,
subject,
text,
html,
}
await this.transport.sendMail(mailOptions)
}
}
export default new EmailSender()
Now you can implement it in your any routes:
router.get('/email', async (req, res) => {
try {
await EmailSender.sendMessage(
'bijaya#bijaya.com',
'Hello world',
'test',
'<h1>Test</h1>'
)
return res.status(200).send('Successfully sent email.')
} catch (exception) {
return res.status(500).send(exception.message)
}
})

Stuck in a express callback hell

I can't cancel the request after sending the email successfully. How can I return properly after sending the email? This might be a callback hell, but I cant figure out how to solve it.
I tried to put some return in different parts but it didn't work.
const router = require('express').Router();
const nodemailer = require('nodemailer');
const emailExistence= require('email-existence');
module.exports = router;
// Send email when user has forgotten his/her password
router.post('/forgetPass', (req, res, next) => {
if(!req.body.email){
next(new Error("Email is required."));
return;
}
emailExistence.check(req.body.email, function(err,res){
if(err || !res){
next(new Error("The email does'nt exist."));
return;
}else{
let transporter = nodemailer.createTransport({
service: 'gmail',
auth: {
user: 'myemail#gmail.com',
pass: 'mypassword'
}
});
let mailOptions = {
from: 'myemail#gmail.com',
to: req.body.email,
subject: 'Link for setting a new password',
html: 'Set a new password'
text: 'email text'
};
transporter.sendMail(mailOptions, function(error, info){
if (error) {
next(new Error("Error in sending email."));
return;
}
res.json(Object.assign(req.base, {
message: "The email has been sent successfully.",
data: info
}));
return;
});
}
});
});
Once you set your response field on successful sending, call next() as a last step, so the next middleware gets the request and sends the response back. So basically:
...
res.json(yourResponse);
next();
...
Or, if this is the last middleware, send the response back to client:
res.send(yourResponse);
I solved it in this way. The emailExistence didn't let me to use promises, so I used email-ckeck instead of it:
const router = require('express').Router();
const nodemailer = require('nodemailer');
const emailExistence= require('email-existence');
var emailCheck = require('email-check');
module.exports = router;
router.post('/forgetPass', (req, res, next) => {
if(!req.body.email){
next(new Error("Email is required."));
return;
}
// Check the req.body.email with email pattern regex
var patt = new RegExp (process.env.EMAIL_PATTERN__REGEX),
isEmail = patt.test(req.body.email);
if(!isEmail){
next(new Error("The email does'nt seem to be a valid email. If you are sure about your email validity contact the website admin."));
return;
}
return emailCheck(req.body.email)
.then(function(result){
let transporter = nodemailer.createTransport({
service: 'gmail',
auth: {
user: process.env.EMAIL,
pass: process.env.EMAILPASSWORD
}
});
let mailOptions = {
from: process.env.EMAIL,
to: req.body.email,
subject: 'Link for setting a new password',
html: 'Set a new password from this link.'
};
return transporter.sendMail(mailOptions)
.then(function (result2) {
res.status(200).json(Object.assign(req.base, {
message: "The email has been sent successfully.",
data: null
}));
return;
},
function(error2){
next(new Error("Error in sending email."));
return;
});
},
function(error) {
next(new Error("The email does'nt seem to be a valid email. If you are sure about your email validity contact the website admin."));
return;
});
});

receive mail with nodemailer without setting "Allow less secure apps to access"

I want to ask something about nodemailer, i make code like
Var client = nodemailer.createTransport ({
Service: 'gmail',
Auth: {
User: 'example#gmail.com', // Your email address
Pass: '123' // Your password},
Tls: {rejectUnauthorized: false}
});
And this works, but after successful delivery, when I have to receive email messages that have been sent, I need to enable gmail settings like "Allow less secure apps to access". I do not want to set it.
So how do I send emails from example#gmail.com TO example1#gmail.com, without setting "Allow less secure apps to access" and message directly accept in email box !!??? Or any other plugin that should be added ??
THANX;)
Obtain accessToken & refreshToken from Google OAuth2.0 Playground,
clientId & clientSecret from Google developer console
const nodemailer = require('nodemailer');
const xoauth2 = require('xoauth2');
var express = require('express');
var router = express.Router();
var smtpTransport = nodemailer.createTransport('SMTP',{
service:"Gmail",
auth:{
XOAuth2: {
user:'sender#emailaddress.com',
clientId: 'your-client-id',
clientSecret: 'your-cliet-secret',
accessToken:'your-access-token',
refreshToken: 'your-refresh-token'
}
}
});
router.get('/emaildemo', function(req, res, next) {
var mailOptions = {
from: 'sender#emailaddress.com',
to: 'xxx#email.com',
subject: 'TEST SUBJECTT',
text: 'TEST MAIL',
};
smtpTransport.sendMail(mailOptions, function(error, info){
if(error){
console.log('Error Occured', error);
return res.send(error);
}
return res.send("mail send successfully");
});
});
module.exports = router;
You need to get the access token, you cannot give it statically.
googleapis can be user for gmail.
for example:
const { google } = require('googleapis');
const { OAuth2 } = google.auth;
const {
MAILING_SERVICE_CLIENT_ID,
MAILING_SERVICE_CLIENT_SECRET,
MAILING_SERVICE_REFRESH_TOKEN,
SENDER_EMAIL_ADDRESS,
OAUTH_PLAYGROUND //https://developers.google.com/oauthplayground
} = process.env;
const oauth2Client = new OAuth2(
MAILING_SERVICE_CLIENT_ID,
MAILING_SERVICE_CLIENT_SECRET,
OAUTH_PLAYGROUND
);
oauth2Client.setCredentials({
refresh_token: MAILING_SERVICE_REFRESH_TOKEN,
});
//>>> get the accessToken
const accessToken = oauth2Client.getAccessToken();
let transporter = nodemailer.createTransport({
service: 'gmail',
auth: {
type: 'OAuth2',
user: SENDER_EMAIL_ADDRESS,
clientId: MAILING_SERVICE_CLIENT_ID,
clientSecret: MAILING_SERVICE_CLIENT_SECRET,
refreshToken: MAILING_SERVICE_REFRESH_TOKEN,
accessToken,
},
});
let mailOptions = {
from: 'no-reply#blah.com',
to: 'to_blah#blah.com',
subject: 'test',
text: 'test'
};
let result = await transporter.sendMail(mailOptions);

Resources