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.
Related
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..
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);
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 send a welcome email after a user signs up with nodemailer and also adding the user to mongodb with passport.authenticate on the same post route. I am able to get this to work separately i.e. either sending email or adding the user to the database but can't seem to get them to work together. I am new to nodejs and would really appreciate any help. Here is the route I am trying to get to work:
router.post('/signup', function(req, res,next) {
async.waterfall([
function(done) {
passport.authenticate('signup', {
successRedirect: '/',
failureRedirect: '/signup',
failureFlash : true
});
},
function(user, done) {
var transporter = nodeMailer.createTransport({
service: 'SendGrid',
auth: {
user: 'user',
pass: 'password'
}
});
var mailOptions = {
to: user.email,
from: 'me#gmail.com',
subject: 'Welcome to the site',
html: '<p> This is html, did I render correctly?</p>'
};
transporter.sendMail(mailOptions, function(err){
done(err);
});
}
], function(err) {
res.redirect('/signup');
});
});
Here is the signup strategy with passport:
var LocalStrategy = require('passport-local').Strategy;
var User = require('../models/user');
var bCrypt = require('bcrypt-nodejs');
module.exports = function(passport){
passport.use('signup', new LocalStrategy({
usernameField : 'email',
passReqToCallback : true
},
function(req, email, password, done) {
findOrCreateUser = function(){
// find a user in Mongo with provided username
User.findOne({ 'email' : email }, function(err, user) {
// In case of any error, return using the done method
if (err){
req.flash('error','Email Already Exists',err.message);
return done(err);
}
// already exists
if (user) {
console.log('User already exists with username:');
return done(null, false, req.flash('error','Email Already Exists'));
} else {
// if there is no user with that email
// create the user
var newUser = new User();
// set the user's local credentials
newUser.password = createHash(password);
newUser.email = req.param('email');
newUser.firstName = req.param('firstName');
newUser.lastName = req.param('lastName');
// save the user
newUser.save(function(err) {
if (err){
console.log('Error in Saving user: '+err);
return done(null, false, req.flash('error',err.message));
}
console.log('User Registration succesful');
return done(null, newUser);
});
}
});
};
// Delay the execution of findOrCreateUser and execute the method
// in the next tick of the event loop
process.nextTick(findOrCreateUser);
})
);
// Generates hash using bCrypt
var createHash = function(password){
return bCrypt.hashSync(password, bCrypt.genSaltSync(10), null);
}
}
Thanks in advance for the help!
Why don't you move the email sending logic to the passport signup strategy?
I am very stuck with a routing error on express.js. I am using jade to render my page views. I have read all the docs and refactored many times to no avail.
This is the plain contents of the link that is pointing to the '/sell' route.
index.jade
include layout
html
button.btn.btn-primary(type='submit', href='/sell') Add Item
When clicking this button the browser returns the following as a 404:
Cannot GET /sell
The problem here is that all other routes work correctly, if you are to modify the above href to other pages i.e. '/' , '/sign_in', etc no error occurs. The problem appears to be isolated to the '/sell' route.
The controller with the '/sell' route is below:
server.js
var dotenv = require('dotenv');
dotenv.load();
var session = require('express-session')
var nodemailer = require('nodemailer');
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
var bcrypt = require('bcrypt');
var async = require('async');
var crypto = require('crypto');
var cookieParser = require('cookie-parser');
var flash = require('express-flash');
var express = require('express');
var app = express();
var mongoose = require('mongoose');
var root = __dirname;
var path = require('path');
var User = require('./models/user');
var bodyParser = require('body-parser');
var errorHelper = require('mongoose-error-helper').errorHelper;
var validator = require('validator');
var Item = require("./models/item")
var username, email, password, owner, product_name, condition, details, price;
mongoose.connect(process.env.MONGODB_CONGO_DEV);
// Middleware
app.set('views', 'app/views');
app.set('view engine', 'jade');
app.set('port', process.env.PORT || 3000);
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true}));
app.use(cookieParser());
app.use(session( {
secret: 'session secret key',
resave: false,
saveUninitialized: true
}));
app.use(flash());
app.use(passport.initialize());
app.use(passport.session());
// Routing
app.get('/', function(req,res) {
res.render('index', {
title: 'Congo',
message: 'Congo',
user: req.user
});
});
app.get('/sell', function(req,res) {
res.render('item', {
title: 'Congo - Sell',
message: 'Sell',
user: req.user
});
});
app.get('/sign_in', function(req,res) {
res.render('sign_in', {
title: 'Congo',
message: 'sign in motherfucker',
user: req.user
});
});
app.post('/sign_in', function(req, res, next) {
passport.authenticate('local', function(err, user, info) {
if (err) return next(err)
if (!user) {
req.flash('error', 'Incorrect login details');
return res.redirect('/sign_in')
};
req.logIn(user, function(err) {
if (err) return next(err);
res.
return res.redirect('/');
});
})(req, res, next);
});
app.get('/sign_up', function(req, res) {
res.render('sign_up', {
title: 'Congo',
message: 'sign up',
user: req.user
});
});
app.post('/sign_up', function(req, res) {
var user = new User({
username: req.body.username,
email: req.body.email,
password: req.body.password
});
if (req.body.confirm != req.body.password) {
req.flash('error', 'Password do not match')
res.redirect('/sign_up')
};
else if (validator.isEmail(req.body.email) === false) {
req.flash('error', 'Invalid email address')
res.redirect('/sign_up')
};
else {
user.save(function(err) {
if (err) {
req.flash('error', 'Email already in use')
res.redirect('/sign_up')
} else {
req.logIn(user, function(err) {
res.redirect('/');
});
}
});
};
});
app.get('/logout', function(req, res){
req.logout();
res.redirect('/');
});
app.get('/forgot', function(req, res) {
res.render('forgot', {
user: req.user,
message: 'you wally'
});
});
app.get('/profile', function(req, res) {
res.render('profile', {
user: req.user,
message: 'User profile'
});
});
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) {
var smtpTransport = nodemailer.createTransport('SMTP', {
service: 'Gmail',
auth: {
user: 'harryandrew.dix#gmail.com',
pass: process.env.GMAIL_PASS
};
});
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');
});
});
app.get('/reset/:token', function(req,res) {
User.findOne({resetPasswordToken: req.params.token, resetPasswordExpires: { $gt: Date.now()}}, function(err,user) {
if (!user) {
req.flash('error', 'Password reset token invalid or expired.');
return res.redirect('/forgot');
}
res.render('reset', {
user: req.user,
message: 'reset dem pass'
});
});
});
app.post('/reset/:token', function(req, res) {
async.waterfall([
function(done) {
User.findOne({ resetPasswordToken: req.params.token, resetPasswordExpires: { $gt: Date.now() } }, function(err, user) {
if (!user) {
req.flash('error', 'Password reset token is invalid or has expired.');
return res.redirect('back');
};
user.password = req.body.password;
user.resetPasswordToken = undefined;
user.resetPasswordExpires = undefined;
user.save(function(err) {
req.logIn(user, function(err) {
done(err, user);
});
});
});
},
function(user, done) {
var smtpTransport = nodemailer.createTransport('SMTP', {
service: 'Gmail',
auth: {
user: 'harryandrew.dix#gmail.com',
pass: process.env.GMAIL_PASS
};
});
var mailOptions = {
to: user.email,
from: 'passwordreset#demo.com',
subject: 'Your password has been changed',
text: 'Hello,\n\n' +
'This is a confirmation that the password for your account ' + user.email + ' has just been changed.\n'
};
smtpTransport.sendMail(mailOptions, function(err) {
req.flash('success', 'Success! Your password has been changed.');
done(err);
});
};
], function(err) {
res.redirect('/');
});
});
// app.post('/add_item', function(req, res) {
// var item = new Item({
// owner: req.user.id,
// product_name: req.body.product_name,
// condition: req.body.condition,
// details: req.body.details,
// price: req.body.price
// });
// item.save(function(err) {
// if (err) {
// req.flash('error', 'Something went wrong, make sure you are signed in.')
// res.redirect('/add_item');
// } else {
// req.logIn(item, function(err) {
// res.redirect('/user_profile');
// });
// };
// });
// });
var server = app.listen(3000, function() {
var host = server.address().address;
var port = server.address().port;
console.log('Example app listening at http://%s:%s',host,port);
});
item.jade
include layout
html
block content
form(method='POST')
legend(style='font-family: Raleway; font-size: 30px;') Item Details
.form-group
label(for='product_name') Product Name
input.form-control(type='text', name='product_name', placeholder='include product name, brand, condition, colour etc.', required autofocus)
.form-group
label(for='condition') Condition
input.form-control(type='text', name='condition', placeholder='e.g. New, Used', required)
.form-group
label(for='image') Image
input.form-control(type='file', name='image')
.form-group
label(for='details') Details
textarea.form-control(name='details', cols='40', rows='5')
.form-group
label(for='price') Price
.input-group
.input-group-addon £
input.form-control(type='text', placeholder='Amount', required)
.input-group-addon .00
br
br
button#btnSubmit.btn.btn-primary(type='submit') Post Item
I think it's acting as form post and tries to reach app.post('/sell')
Change it:
button.btn.btn-primary(type='submit', href='/sell') Add Item
to:
a.btn.btn-primary(href='/sell') Add Item
also remove some parts of middleware and keep only these lines:
app.set('views', 'app/views');
app.set('view engine', 'jade');
app.set('port', process.env.PORT || 3000);
app.use(logger('dev'));
and then navigate in Your browser to /sell route, check if it's working.
if Yes - so problem with one of middlewares that we have deleted to check.
You should add a logger middleware and see what error the server gives when you try to go to that route. Another thing to try would be to rename the sell route and see if that works. That could indicate a conflict with another one of your routes, although it's not clear where this would be from looking at your code.