Nodemailer error: verification email doesn't works - node.js

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

Related

Nodemailer sending emails but getting ERR_HTTP_HEADERS_SENT

I don't why i'm getting this every time I try to use Nodemailer to send emails:
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent
to the client
at new NodeError (node:internal/errors:371:5)
at ServerResponse.setHeader (node:_http_outgoing:576:11)
the thing is it's working but i'm getting this error, by working I mean It sends the email.
The Code:
const sendEmail = async (email, subject, payload, template) => {
try {
// create reusable transporter object using the default SMTP transport
const transporter = nodemailer.createTransport({
service: "gmail",
auth: {
user: process.env.EMAIL_USERNAME,
pass: process.env.EMAIL_PASSWORD,
},
});
const options = () => {
return {
from: process.env.FROM_EMAIL,
to: email,
subject: subject,
html: template,
};
};
// Send email
transporter.sendMail(options(), (error, info) => {
if (error) {
return error;
} else {
return res.status(200).json({
success: true,
});
}
});
} catch (error) {
throw error;
}
};
router.post("/", async (req, res, next) => {
try {
if (!req.body.newsletterEmail) {
return next(createError(400, "Please enter your email"));
} else if (!req.body.newsletterEmail || !isEmail(req.body.newsletterEmail)) {
return next(createError(400, "Please enter a valid email address"));
}
const checkEmail = await SubscribedEmails.findOne({ newsletterEmail: req.body.newsletterEmail });
if (checkEmail) {
return next(createError(400, "Email is already subscribed"));
} else {
//create new user
const newSubscribedEmail = new SubscribedEmails ({
newsletterEmail: req.body.newsletterEmail,
});
//save user and respond
const SubscribedEmail = await newSubscribedEmail.save();
res.status(200).json({
message: "Successfully subscribed",
SubscribedEmail,
});
const link = `${process.env.CLIENT_URL}/`;
await sendEmail(
SubscribedEmail.newsletterEmail,
"Welcome to our newsletter!",
{
link: link,
},
`<div> <p>Hi,</p> <p>You are subscribed to our.</p> <p> Please click the link below to view more</p> <a href=${link}>GO</a> </div>`
);
return res.status(200).send(link);
}
} catch(err) {
next(err);
}
});
This is likely due to some quirks with using Gmail with Nodemailer. Review the docs here - https://nodemailer.com/usage/using-gmail/
and configure your Gmail account here -
https://myaccount.google.com/lesssecureapps
Finally, you may run into issues with Captcha. Configure here - https://accounts.google.com/DisplayUnlockCaptcha

GaxiosError: invalid_grant in my nodejs app while i'm sending mail with nodemailer package using google api

I want to verify user email when user register in my nodejs backend app i'm using the auth2.0 i'm providing the client id, secret but response giving me error
(GaxiosError: invalid_grant
at Gaxios._request (/home/amir/Desktop/Nodejs/Auth/backend/node_modules/gaxios/build/src/gaxios.js:84:23)
at processTicksAndRejections (node:internal/process/task_queues:96:5)
at async OAuth2Client.refreshTokenNoCache (/home/amir/Desktop/Nodejs/Auth/backend/node_modules/google-auth-library/build/src/auth/oauth2client.js:172:21)
at async OAuth2Client.refreshAccessTokenAsync (/home/amir/Desktop/Nodejs/Auth/backend/node_modules/google-auth-library/build/src/auth/oauth2client.js:196:19)
at async OAuth2Client.getAccessTokenAsync (/home/amir/Desktop/Nodejs/Auth/backend/node_modules/google-auth-library/build/src/auth/oauth2client.js:216:23) )
I'm giving the refresh_token also but its not working i don't know where i'm doing wrong Please anyone can resolve it
This is my code or api's to register user
exports.registerHandle = (req, res) => {
const { name, email, password, password2 } = req.body;
let errors = [];
//------------ Checking required fields ------------//
if (!name || !email || !password || !password2) {
errors.push({ msg: 'Please enter all fields' });
}
//------------ Checking password mismatch ------------//
if (password != password2) {
errors.push({ msg: 'Passwords do not match' });
}
//------------ Checking password length ------------//
if (password.length < 8) {
errors.push({ msg: 'Password must be at least 8 characters' });
}
if (errors.length > 0) {
res.render('register', {
errors,
name,
email,
password,
password2
});
} else {
//------------ Validation passed ------------//
User.findOne({ email: email }).then(user => {
if (user) {
//------------ User already exists ------------//
errors.push({ msg: 'Email ID already registered' });
res.render('register', {
errors,
name,
email,
password,
password2
});
} else {
const oauth2Client = new OAuth2(
"My ID", // ClientID
"MY Secret", // Client Secret
"https://developers.google.com/oauthplayground" // Redirect URL
);
oauth2Client.setCredentials({
refresh_token: "1%2F%2F04T_nqlj9UVrVCgYIARAAGAQSNwF-L9IrGm-NOdEKBOakzMn1cbbCHgg2ivkad3Q_hMyBkSQen0b5ABfR8kPR18aOoqhRrSlPm9w"
});
const accessToken = oauth2Client.getAccessToken()
console.log('Google Access Token ===', accessToken);
const token = jwt.sign({ name, email, password }, JWT_KEY, { expiresIn: '30m' });
console.log('Token ', token);
const CLIENT_URL = 'http://' + req.headers.host;
const output = `
<h2>Please click on below link to activate your account</h2>
<p>${CLIENT_URL}/auth/activate/${token}</p>
<p><b>NOTE: </b> The above activation link expires in 30 minutes.</p>
`;
const transporter = nodemailer.createTransport({
service: 'gmail',
auth: {
type: "OAuth2",
user: "mygmail#gmail.com",
clientId: "My ID",
clientSecret: "My Secret",
refreshToken: "1%2F%2F04T_nqlj9UVrVCgYIARAAGAQSNwF-L9IrGm-NOdEKBOakzMn1cbbCHgg2ivkad3Q_hMyBkSQen0b5ABfR8kPR18aOoqhRrSlPm9w",
accessToken: accessToken
},
});
// send mail with defined transport object
const mailOptions = {
from: '"Auth Admin" <mygmail#gmail.com>', // sender address
to: email, // list of receivers
subject: "Account Verification: NodeJS Auth ✔", // Subject line
generateTextFromHTML: true,
html: output, // html body
};
transporter.sendMail(mailOptions, (error, info) => {
if (error) {
console.log(error);
req.flash(
'error_msg',
'Something went wrong on our end. Please register again.'
);
res.redirect('/auth/login');
}
else {
console.log('Mail sent : %s', info.response);
req.flash(
'success_msg',
'Activation link sent to email ID. Please activate to log in.'
);
res.redirect('/auth/login');
}
})
}
});
}
}
Invalid_grant can have a lot of causes. The most common cause is the refresh token expiring. Apps that are in testing have their consent revoked after seven days this causes the refresh tokens to expire.
You appear to be loading the current access token from the authorization request
const accessToken = oauth2Client.getAccessToken()
Yet you also appear to have a hard coded refresh token
refreshToken: "1%2F%2F04T_nqlj9UVrVCgYIARAAGAQSNwF-L9IrGm-NOdEKBOakzMn1cbbCHgg2ivkad3Q_hMyBkSQen0b5ABfR8kPR18aOoqhRrSlPm9w",
Why not store the latest refresh token instead?

Unable to send mail through node mailer. Error: connect ECONNREFUSED at port 25"

Every time I make a forgot password request, it gives me this error and the mail is never sent to the Mailtrap.
Nodemailer Setup
This is the basic node mailer setup I did.
port = 25
const nodemailer = require('nodemailer');
const sendEmail = async (options) => {
// 1) Create a Transporter
const transporter = nodemailer.createTransport({
host: process.env.EMAIL_HOST,
port: process.env.EMAIL_PORT,
auth: {
user: process.env.EMAIL_USERNAME,
pass: process.env.EMAIL_PASSWORD,
},
});
// 2) Define the email Options
const mailOptions = {
from: 'Sachin Yadav <sachin.yadav#gmail.com',
to: options.email,
subject: options.subject,
text: options.text,
};
// 3) Send the email
await transporter.sendMail(mailOptions);
};
Forgot Password Function
catchAsync is just a wrapper function which is created just to catch asynchronous errors separately. It returns the async function passed into it and call it with the req, res and next parameters.
exports.forgotPassword = catchAsync(async (req, res, next) => {
// 1) Get user based on posted email
const user = await User.findOne({ email: req.body.email });
if (!user)
return next(
new AppError('No user with that email. Please try again!', 404)
);
// 2) Generate Random
const resetToken = user.createPasswordResetToken();
await user.save({ validateBeforeSave: false });
// 3) Send back the token on email
const resetURL = `${req.protocol}://${req.get('host')}/api/v1/users/resetPassword/${resetToken})}`;
const message = `Forgot your password? Submit a PATCH request with your new password and passwordConfirm to : ${resetURL}`;
// Send Email
try {
await sendEmail({
email: user.email,
subject: 'Password reset link (Valid for 10mins)',
message,
});
res.status(200).json({
status: 'success',
message: 'Token send',
});
// Err
} catch (err) {
// Set back the token and expire time
user.createPasswordResetToken = undefined;
user.passwordResetExpires = undefined;
await user.save({ validateBeforeSave: false });
return next(
new AppError(`There was an error sending the email ${err.message}`, 500)
);
}
});
I just changed the port from 25 to 2525 and for some reason it worked. If anyone know why this worked, please let me know.
One reason could be some other application is using port '25' in your system. If you're using a Windows PC you can check the ports that are being used with the below command(you need to open cmd as an administrator):
netstat -aon

Implement a mailing solution in Express nodejs

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

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;
});
});

Resources