I am trying to perform forgot password operation, i.e. trying to send a mail to the user to change the password via a reset password link contained in the mail.
I am using .env file to store my username, passwords, and added it to gitigonre.
javascript post code:-
app.post("/forgot", function (req, res, next) {
async.waterfall(
[
function (done) {
crypto.randomBytes(20, function (err, buf) {
var token = buf.toString("hex");
done(err, token);
});
},
function (token, done) {
User.findOne({ email: req.body.email }, function (err, user) {
if (!user) {
req.flash("error", "No account with that email address exists.");
return res.redirect("/forgot");
}
// app.get('/reset');
user.resetPasswordToken = token;
user.resetPasswordExpires = Date.now() + 3600000; // 1 hour
user.save(function (err) {
done(err, token, user);
});
});
},
function (token, user, done) {
var smtpTransport = nodemailer.createTransport({
host: "smtp.gmail.com",
service: "gmail",
auth: {
xoauth2: xoauth2.createXOAuth2Generator({
type: "OAuth2",
user: process.env.Gmail_username,
clientSecret: process.env.Gmail_password,
}),
},
tls: {
ciphers: "SSLv3",
},
});
var mailOptions = {
from: "passwordreset#demo.com",
to: user.email,
subject: "Node.js Password Reset",
text:
"You are receiving this because you (or someone else) have requested the reset of the password for your account.\n\n" +
"Please click on the following link, or paste this into your browser to complete the process:\n\n" +
"http://" +
req.headers.host +
"/reset/" +
token +
"\n\n" +
"If you did not request this, please ignore this email and your password will remain unchanged.\n",
};
smtpTransport.sendMail(mailOptions, function (err) {
req.flash(
"info",
"An e-mail has been sent to " +
user.email +
" with further instructions."
);
done(err, "done");
});
},
],
function (err) {
if (err) return next(err);
res.redirect("/forgot");
}
);
});
I have allowed access for the less secure apps for the email address too. Client secret key is as generated by google account, it's not the password of my gmail account, just named after that. Can someone suggest, what I am doing wrong..
Related
I've been following a guide on implementing a password reset in Node. I'm working on the password reset post route below and when I post nothing seems to happen, it justs reloads the reset page. ive added some console.log() in various places on the code but they dont get executed. Hope someone can assist. ive deleted the other routes to make the code shorter
var express = require('express');
var router = express.Router({ mergeParams: true });
var Kids = require('../models/kid');
var User = require('../models/user');
var async = require('async');
var nodemailer = require('nodemailer');
var crypto = require('crypto');
var middleware = require('../middleware');
//password reset
router.get('/password_reset', function (req, res) {
res.render('password_reset');
});
//posting the account email reset
router.post('/password_reset', function (req, res, next) {
async.waterfall([
function(done) {
crypto.randomBytes(20, function(err, buf) {
var token = buf.toString('hex');
done(err, token);
});
},
function(token, done) {
User.findOne({ email: req.body.email }, function(err, user) {
if (!user) {
req.flash('error', 'No account with that email address exists.');
return res.redirect('/forgot');
}
user.resetPasswordToken = token;
user.resetPasswordExpires = Date.now() + 3600000; // 1 hour
user.save(function(err) {
done(err, token, user);
});
});
},
function(token, user, done) {
var smtpTransport = nodemailer.createTransport('SMTP', {
service: 'SendGrid',
auth: {
user: 'Hidden',
pass: 'Hidden'
}
});
var mailOptions = {
to: user.email,
from: 'passwordreset#demo.com',
subject: 'Node.js Password Reset',
text: 'You are receiving this because you (or someone else) have requested the reset of the password for your account.\n\n' +
'Please click on the following link, or paste this into your browser to complete the process:\n\n' +
'http://' + req.headers.host + '/reset/' + token + '\n\n' +
'If you did not request this, please ignore this email and your password will remain unchanged.\n'
};
smtpTransport.sendMail(mailOptions, function(err) {
req.flash('info', 'An e-mail has been sent to ' + user.email + ' with further instructions.');
done(err, 'done');
});
}
], function(err) {
if (err) return next(err);
res.redirect('/forgot_reset');
});
});
module.exports = router;
i figured out the issue, it was to do with my form. I had forgotten to add:
<form method="post" action="/password_reset">
so the form was not sending anything.
I've got an assignment, and they have asked me to obtain user credentials using OAuth 2.0 and save it in a database (say MongoDB). I was able to complete this step.
The second task is to have an API endpoint to execute send emails using the credentials previous stored. (Using Gmail REST API). I am struggling with this second task and have been searching all over the internet.
PS: I'm doing it in Nodejs
Documentation : https://developers.google.com/gmail/api/quickstart/nodejs
Setup Gmail REST API and get your clientId and client secret
write a function to send email
i have used nodemailar in example, should be similar to gmail
var nodemailer = require('nodemailer');
var crypto = require('crypto');
let transporter = nodemailer.createTransport({
host: 'smtp.sendgrid.net',
port: 465,
auth: {
user: 'apikey',
pass: 'Your Key'
}
});
function generateVerificationToken() {
return crypto.randomBytes(16).toString('hex');
}
exports.addUserEmail = function (req, res) {
Users.find({ email: req.body.email }, (err, users) => {
if (err) { res.send("Err"); return }
if (users.length == 0) {
var user = new Users(req.body);
user.verification.verification_token = generateVerificationToken();
user.save(function (err, email) {
if (err)
res.send(err);
sendVerificationEmail(req, user.verification.verification_token, email.email, (err) => {
if (err) { console.log(err); return res.status(500).send({ msg: err.message }); }
res.status(200).send({
'A verification email has been sent to ' : email.email
});
});
});
}
else {
res.status(400).send({ Error : 'Invalid Request',
Error : 'This email is already Registered'
});
}
});
};
function sendVerificationEmail(req, token, email, cb) {
let emailText = 'Hello,\n\n' + 'Please verify your account by clicking the link: \nhttp:\/\/' + req.headers.host + '\/user\/verify\/?token=' + token + '&email=' + email + '.\n';
var mailOptions = {
from: 'donotreply#Express-Server.com',
to: email,
subject: 'Account Verification Token',
text: emailText
};
transporter.sendMail(mailOptions, function (err) {
cb(err);
});
}
In your route
app.route('/user/register')
.post(users.addUserEmail);
tried to setup a password recovery in my nodejs project using nodemailer.I have got 'no recipients defined' error. how to fix the code?.
here is my code:
app.post('/forgotpass', (req, res, next)=> {
let recoveryPassword = '';
async.waterfall([
(done) => {
crypto.randomBytes(20, (err , buf) => {
let token = buf.toString('hex');
done(err, token);
});
},
(token, done) => {
User.findOne({username : req.body.username})
.then(user => {
if(!user) {
console.log("user does not exists");
return res.redirect('/forgotpass');
}
user.resetPasswordToken = token;
user.resetPasswordExpires = Date.now() + 1800000; // 1/2 hours
user.save(err => {
done(err, token, user);
});
});
},
(token, user) => {
let smtpTransport = nodemailer.createTransport({
host: 'smtp.gmail.com',
port: 465,
auth: {
user: 'test#gmail.com',
pass: 'testpass'
}
});
let mailOptions = {
to: user.email,
from : 'Ghulam Abbas myapkforest#gmail.com',
subject : 'Recovery Email from Auth Project',
text : 'Please click the following link to recover your passoword: \n\n'+
'http://'+ req.headers.host +'/reset/'+token+'\n\n'+
'If you did not request this, please ignore this email.'
};
smtpTransport.sendMail(mailOptions, err=> {
console.log(err);
res.redirect('/forgotpass');
});
}
], err => {
if(err) res.redirect('/forgotpass');
if (err )console.log(err);
});
});
and here is the error I got :
Error: No recipients defined
at SMTPConnection._formatError (C:\Users\JITHENDRA\Desktop\project\node\Secrets-Starting-Code\node_modules\nodemailer\lib\smtp-connection\index.js:784:19)
at SMTPConnection._setEnvelope (C:\Users\JITHENDRA\Desktop\project\node\Secrets-Starting-Code\node_modules\nodemailer\lib\smtp-connection\index.js:995:34)
at SMTPConnection.send (C:\Users\JITHENDRA\Desktop\project\node\Secrets-Starting-Code\node_modules\nodemailer\lib\smtp-connection\index.js:615:14)
at sendMessage (C:\Users\JITHENDRA\Desktop\project\node\Secrets-Starting-Code\node_modules\nodemailer\lib\smtp-transport\index.js:227:28)
at C:\Users\JITHENDRA\Desktop\project\node\Secrets-Starting-Code\node_modules\nodemailer\lib\smtp-transport\index.js:285:25
at SMTPConnection._actionAUTHComplete (C:\Users\JITHENDRA\Desktop\project\node\Secrets-Starting-Code\node_modules\nodemailer\lib\smtp-connection\index.js:1537:9)
at SMTPConnection.<anonymous> (C:\Users\JITHENDRA\Desktop\project\node\Secrets-Starting-Code\node_modules\nodemailer\lib\smtp-connection\index.js:550:26)
at SMTPConnection._processResponse (C:\Users\JITHENDRA\Desktop\project\node\Secrets-Starting-Code\node_modules\nodemailer\lib\smtp-connection\index.js:942:20)
at SMTPConnection._onData (C:\Users\JITHENDRA\Desktop\project\node\Secrets-Starting-Code\node_modules\nodemailer\lib\smtp-connection\index.js:749:14)
at TLSSocket.SMTPConnection._onSocketData (C:\Users\JITHENDRA\Desktop\project\node\Secrets-Starting-Code\node_modules\nodemailer\lib\smtp-connection\index.js:195:44)
at TLSSocket.emit (events.js:310:20)
at addChunk (_stream_readable.js:286:12)
at readableAddChunk (_stream_readable.js:268:9)
at TLSSocket.Readable.push (_stream_readable.js:209:10)
at TLSWrap.onStreamRead (internal/stream_base_commons.js:186:23) {
code: 'EENVELOPE',
command: 'API'
}
that's the error I have got.please fix the code
I just want the node mailer to send the mail after verifying the user exists or not
This error means that you aren't passing a mailOptions.to value, so the user.email value is empty or not a valid email address. If you utilize async/await you can write the code more concisely and avoid using async.waterfall to make it easier to debug. There are a few changes/typos noted below -
// define transport here, outside the handler
const smtpTransport = nodemailer.createTransport({
host: 'smtp.gmail.com',
port: 465,
auth: {
user: 'test#gmail.com',
pass: 'testpass'
}
});
// use async function as the handler
app.post('/forgotpass', async (req, res) => {
try {
const user = await User.findOne({username : req.body.username});
if (!user) {
console.log("user does not exists");
return res.redirect('/forgotpass');
}
// make sure you have a valid email here
console.log(`Sending email to ${user.email}`);
// get token here once we have a valid user
const token = crypto.randomBytes(20);
// update the user with the token
user.resetPasswordToken = token;
user.resetPasswordExpires = Date.now() + 1800000; // 1/2 hours
await user.save();
// send the email
await smtpTransport.sendMail({
to: user.email,
from : 'Ghulam Abbas <myapkforest#gmail.com>', // <-- wrap email address in <>
subject : 'Recovery Email from Auth Project',
text : 'Please click the following link to recover your passoword: \n\n'+ // <-- typo: "passoword" should be "password"
'http://'+ req.headers.host +'/reset/'+token+'\n\n'+ // <-- use HTTPS!
'If you did not request this, please ignore this email.'
});
} catch (err) {
// catch all exceptions here...
console.log(err);
}
// everything redirects to the GET handler for forgotpass
return res.redirect('/forgotpass');
});
I'm using Nodemailer for sending a forget password mail with Gmail service.I tried to reach to the same error earlier in the StackOverflow, but I couldn't find the solution. Please help me, I have no idea why it is giving error like,
"TypeError: Cannot create property 'mailer' on string 'smtpTransport'"
Here is my code below-
var nodemailer = require('nodemailer');
app.post('/forgot', function(req, res, next) {
async.waterfall([
function(done) {
crypto.randomBytes(20, function(err, buf) {
var token = buf.toString('hex');
done(err, token);
});
},
function(token, done) {
User.findOne({ email: req.body.email }, function(err, user) {
if (!user) {
req.flash('error', 'No account with that email address exists.');
return res.redirect('/forgot');
}
user.resetPasswordToken = token;
user.resetPasswordExpires = Date.now() + 3600000; // 1 hour
user.save(function(err) {
done(err, token, user);
});
});
},
function(token, user, done) {
console.log(token, "Token");
console.log(user, "user")
var smtpTransport = nodemailer.createTransport('SMTP', {
service: 'gmail',
auth: {
user: 'abc#gmail.com',
pass: '123456'
}
});
var mailOptions = {
to: user.email,
from: 'myproducts#mailinator.com',
subject: 'My Products Password Reset',
text: 'You are receiving this because you (or someone else) have requested the reset of the password for your account.\n\n' +
'Please click on the following link, or paste this into your browser to complete the process:\n\n' +
'http://' + req.headers.host + '/reset/' + token + '\n\n' +
'If you did not request this, please ignore this email and your password will remain unchanged.\n'
};
smtpTransport.sendMail(mailOptions, function(err) {
req.flash('info', 'An e-mail has been sent to ' + user.email + ' with further instructions.');
done(err, 'done');
});
}
], function(err) {
if (err) return next(err);
res.redirect('/forgot');
});
});
And the error is something like this-
/home/cis/Desktop/myproducts/node_modules/mongodb/lib/utils.js:132
throw err;
^
TypeError: Cannot create property 'mailer' on string 'smtpTransport'
at Mail (/home/cis/Desktop/myproducts/node_modules/nodemailer/lib/mailer/index.js:45:33)
at Object.module.exports.createTransport (/home/cis/Desktop/myproducts/node_modules/nodemailer/lib/nodemailer.js:52:14)
at /home/cis/Desktop/myproducts/app.js:185:38
at nextTask (/home/cis/Desktop/myproducts/node_modules/async/dist/async.js:5310:14)
at next (/home/cis/Desktop/myproducts/node_modules/async/dist/async.js:5317:9)
at /home/cis/Desktop/myproducts/node_modules/async/dist/async.js:958:16
at /home/cis/Desktop/myproducts/app.js:177:11
at /home/cis/Desktop/myproducts/node_modules/mongoose/lib/model.js:3913:16
at model.$__save.error (/home/cis/Desktop/myproducts/node_modules/mongoose/lib/model.js:342:7)
at /home/cis/Desktop/myproducts/node_modules/kareem/index.js:297:21
at next (/home/cis/Desktop/myproducts/node_modules/kareem/index.js:209:27)
at Kareem.execPost (/home/cis/Desktop/myproducts/node_modules/kareem/index.js:217:3)
at _cb (/home/cis/Desktop/myproducts/node_modules/kareem/index.js:289:15)
at $__handleSave (/home/cis/Desktop/myproducts/node_modules/mongoose/lib/model.js:280:5)
at /home/cis/Desktop/myproducts/node_modules/mongoose/lib/model.js:208:9
at args.push (/home/cis/Desktop/myproducts/node_modules/mongodb/lib/utils.js:404:72)
[nodemon] app crashed - waiting for file changes before starting...
The Nodemailer structure has been changed, try use this :
smtpTransport = nodemailer.createTransport({
service: 'Gmail',
auth: {
xoauth2: xoauth2.createXOAuth2Generator({
user: 'youremail#gmail.com',
//and other stuff here
});
}
});
var nodemailer = require("nodemailer");
var smtpTransport = nodemailer.createTransport({
service: "Yahoo", // sets automatically host, port and connection security settings
auth: {
user: "xxxxxxxxxx95#yahoo.com",
pass: "xxxxxxxxxxxx"
}
});
function mail(messageBody) {
let messageBodyJson = JSON.stringify(messageBody)
smtpTransport.sendMail({ //email options
from: "xxxxxxxxxx95#yahoo.com", // sender address. Must be the same as authenticated user if using Gmail.
to: "xxxxxxxxxx95#gmail.com", // receiver
subject: "Emailing with nodemailer", // subject
text: messageBodyJson // body
}, function(error, response){ //callback
if(error){
console.log("error",error);
}else{
console.log(response);
}
// smtpTransport.close(); // shut down the connection pool, no more messages. Comment this line out to continue sending emails.
});
}
mail("your mail message");
Try this.
Link to a similar question
Gmail / Google app email service requires OAuth2 for authentication. PLAIN text password will require disabling security features manually on the google account.
To use OAuth2 in Nodemailer, refer: https://nodemailer.com/smtp/oauth2/
Sample code:
var email_smtp = nodemailer.createTransport({
host: "smtp.gmail.com",
auth: {
type: "OAuth2",
user: "youremail#gmail.com",
clientId: "CLIENT_ID_HERE",
clientSecret: "CLIENT_SECRET_HERE",
refreshToken: "REFRESH_TOKEN_HERE"
}
});
I am trying to implement passport local strategy in my project following this blog.
I have created the passportjs file in config .
var passport = require('passport'),
LocalStrategy = require('passport-local').Strategy;
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user);
});
});
passport.use(new LocalStrategy({
usernameField: 'username',
passwordField: 'password'
},
function(username, password, done) {
console.log("User Authenticating...." + username + 'and' + password);
User.findOne({
username: username
}).exec(function(err, user) {
if (err) {
return done(err);
}
if (!user) {
return done(null, false, {
message: 'Unknown user ' + username
});
}
if (user.password != password) {
return done(null, false, {
message: 'Invalid password'
});
}
return done(null, user);
});
}
));
i have updated the httpjs as per suggestion
middleware: {
passportInit : require('passport').initialize(),
passportSession : require('passport').session(),
order: [
'startRequestTimer',
'cookieParser',
'session',
'passportInit',
'passportSession',
'myRequestLogger',
'bodyParser',
'handleBodyParserError',
'compress',
'methodOverride',
'poweredBy',
'router',
'www',
'favicon',
'404',
'500'
]
}
/***************************************************************************
* *
* The number of seconds to cache flat files on disk being served by *
* Express static middleware (by default, these files are in `.tmp/public`) *
* *
* The HTTP static cache is only active in a 'production' environment, *
* since that's the only time Express will cache flat-files. *
* *
***************************************************************************/
// cache: 31557600000
};
Now when I am trying to call local strategy from my Auth controller as
login: function (req, res) {
var user = req.body.user;
// check user
if (!user) {
//send bad request
return res.status(500).json({payload : {}, message : "Undefined user"});
}
var username = (user.username !== undefined)? user.username : false;
var password = (user.password != undefined)? user.password : false;
// Grab user fields.
if (!username || !password) {
//send bad request
return res.status(400).json({payload : {}, message : "Invalid username or password"});
}
console.log("before authenticated");
console.log("username="+username);
console.log("password="+password);
passport.authenticate('local', function(err, user, info) {
console.log("User Authenticated");
if (err) {
console.log("error=errpr");
return res.status(400).json({payload : {error: err}, message : info.message});
}
if (!user) {
console.log("error=user");
return res.status(400).json({payload : {error: err}, message : info.message});
}
console.log("User Authenticated2");
_authTokenRequestCb(user,req,res);
})(req,res);
},
It never authenticate the user and the console log statement in Paasportjs.
console.log("User Authenticating...."+username+'and'+password);
also never executed.I am not getting any error also .
Can anybody suggest where I am getting it wrong.
Update:I have found in this stack question "If 'email' or 'password' are not provided passport just automatically fails. I guess some documentation of that would have been useful!."
But as console log statement giving me the username and passwoprd (in the registerblock
console.log("before authenticated");
console.log("username="+username);
console.log("password="+password);
So I am sure this is not the case.
Thanks
Did you create a policy file to say which controller actions are to be authenticated? Passport doesn't have a policy to deny access to a controller. For this, you have to create another policy.
Just have a look at this answer: Passport authentication not working in Sails JS application
Let me know if this doesn't help.