Combine two functions into one Stripe and Passport - javascript

I currently have it set so a user signs up with just their email and password. Simple and easy, I want them to be able to select a plan from a select element. I know how to access the part of the request I want using req.body.plan if the name for the select is plan.
I have two separate controller functions currently to do this in a bit different way. I have it so the postSignup signs the user up using passport.js and everything is all good. I also have postPlan that when the user is on /billing and selects a plan then submits the form that they have that plan assigned to them. The part I am stuck on is the User.findById(req.user.id, function(err, user) { part, more specifically the req.user.id. How can I take the two functions below and have them combined so that is sets the user's plan based off the select element in the signup form.
postPlan
exports.postPlan = function(req, res, next){
var plan = req.body.plan;
console.log("plan: ",req.body.plan);
var stripeToken = null;
if(plan){
plan = plan.toLowerCase();
}
if(req.user.stripe.plan == plan){
req.flash('info', {msg: 'The selected plan is the same as the current plan.'});
return res.redirect(req.redirect.success);
}
if(req.body.stripeToken){
stripeToken = req.body.stripeToken;
}
if(!req.user.stripe.last4 && !req.body.stripeToken){
req.flash('errors', {msg: 'Please add a card to your account before choosing a plan.'});
return res.redirect(req.redirect.failure);
}
User.findById(req.user.id, function(err, user) {
if (err) return next(err);
user.setPlan(plan, stripeToken, function (err) {
var msg;
if (err) {
if(err.code && err.code == 'card_declined'){
msg = 'Your card was declined. Please provide a valid card.';
} else if(err && err.message) {
msg = err.message;
} else {
msg = 'An unexpected error occurred.';
}
req.flash('errors', { msg: msg});
return res.redirect(req.redirect.failure);
}
req.flash('success', { msg: 'Plan has been updated.' });
res.redirect(req.redirect.success);
});
});
};
postSignup
exports.postSignup = function(req, res, next){
req.assert('email', 'Please sign up with a valid email.').isEmail();
req.assert('password', 'Password must be at least 6 characters long').len(6);
var errors = req.validationErrors();
if (errors) {
req.flash('errors', errors);
req.flash('form', {
email: req.body.email
});
return res.redirect('/signup');
}
// calls next middleware to authenticate with passport
passport.authenticate('signup', {
successRedirect: '/dashboard', // Select redirect for post signup
failureRedirect: '/signup',
failureFlash : true
})(req, res, next);
next();
};
Current Idea
exports.postSignup = function(req, res, next){
req.assert('email', 'Please sign up with a valid email.').isEmail();
req.assert('password', 'Password must be at least 6 characters long').len(6);
var errors = req.validationErrors();
if (errors) {
req.flash('errors', errors);
req.flash('form', {
email: req.body.email
});
return res.redirect('/signup');
}
// calls next middleware to authenticate with passport
passport.authenticate('signup', {
successRedirect: '/dashboard', // Select redirect for post signup
failureRedirect: '/signup',
failureFlash : true
});
// var plan = req.body.plan;
var plan = 'silver';
var stripeToken = null;
if(req.body.stripeToken){
stripeToken = req.body.stripeToken;
}
User.findById({"email":req.body.email}, function(err, user) {
if (err) return next(err);
console.log("Ran here");
user.setPlan(plan, stripeToken, function (err) {
var msg;
if (err) {
if(err && err.message) {
msg = err.message;
} else {
msg = 'An unexpected error occurred.';
}
req.flash('errors', { msg: msg});
return res.redirect(req.redirect.failure);
}
req.flash('success', { msg: 'Thanks for signing up! ' });
res.redirect(req.redirect.success);
});
});
(req, res, next);
next();
};
Error message for current attempt above
{
"message": "Cast to ObjectId failed for value \"{ email: 'snappierjaguar#gmail.com' }\" at path \"_id\" for model \"User\"",
"error": {
"message": "Cast to ObjectId failed for value \"{ email: 'snappierjaguar#gmail.com' }\" at path \"_id\" for model \"User\"",
"name": "CastError",
"stringValue": "\"{ email: 'snappierjaguar#gmail.com' }\"",
"kind": "ObjectId",
"value": {
"email": "snappierjaguar#gmail.com"
},
"path": "_id"
}
}

Related

Cannot set headers after they are sent to the client error

I have a login form that authenticates using postgresql I'm trying to check if users exists then redirect the client to the other page. The code is:
app.post('/login', (req, res) => {
var Enteredusername = req.body.username;
var Enteredpassword = req.body.password;
pool.query("SELECT * FROM tbl_users WHERE username = $1 AND password = $2", [Enteredusername, Enteredpassword], (err, result) => {
if (err) return console.log('error in query', err);
// need to check if user exists
let user = (result.rows.length > 0) ? result.rows[0] : null;
if (!user) {
req.flash('notify', 'This is a test notification.')
res.render('login', {
messages: req.flash('Username or Password is incorrect !')
});
return res.redirect('/login')
}
res.redirect('/posts')
});
});
And I got the error:
_http_outgoing.js:470
throw new ERR_HTTP_HEADERS_SENT('set');
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client.
How Can I fix it?
It's the async behavior of javascript res.redirect('/posts') is executed before req.flash and res.render you can hack it like this :
req.session.userId = Enteredusername;
if (!user) {
req.flash('notify', 'This is a test notification.')
res.render('login', {
messages: req.flash('Username or Password is incorrect !')
});
return res.redirect('/login')
}else{
return res.redirect('/posts')
}

Bcrypt reset the hash and the passwords of the users are not good anymore each time the users POST data

I made a login with bcrypt.
I also made a page where users can edit their information, like their bio etc.
Each time an user edit his bio on this page the hash from bcrypt change, which is normal i suppose, but the user login back, the password is wrong...
I used the same model for mongoDb for the user when he log in and when he edit his data.
I started node.js recently so I apologize if my question is stupid,,,
The controller code with the Post :
app.post('/settings-user', mid.requiresLogin, function(req, res, next){
User.findById(req.session.userId, function (err, user) {
// todo: don't forget to handle err
if (!user) {
return res.redirect('/edit');
}
// good idea to trim
var bio = req.body.bio.trim();
// validate
if (!bio) { // simplified: '' is a falsey
req.flash('error', 'One or more fields are empty');
return res.redirect('/settings-user'); // modified
}
// no need for else since you are returning early ^
user.bio = bio;
// don't forget to save!
user.save(function (err) {
// todo: don't forget to handle err
res.redirect('/settings-user/');
});
});
});
The User model :
app.post('/settings-user', mid.requiresLogin, function(req, res, next){
User.findById(req.session.userId, function (err, user) {
// todo: don't forget to handle err
if (!user) {
return res.redirect('/edit');
}
// good idea to trim
var bio = req.body.bio.trim();
// validate
if (!bio) { // simplified: '' is a falsey
req.flash('error', 'One or more fields are empty');
return res.redirect('/settings-user'); // modified
}
// no need for else since you are returning early ^
user.bio = bio;
// don't forget to save!
user.save(function (err) {
// todo: don't forget to handle err
res.redirect('/settings-user/');
});
});
});
The User model :
var mongoose = require('mongoose');
var bcrypt = require('bcrypt');
var UserSchema = new mongoose.Schema({
email: {
type: String,
unique: true,
required: true,
trim: true
},
name: {
type: String,
required: true,
trim: true
},
password: {
type: String,
required: true
},
bio: {
type: String
}
});
// authenticate input against database documents
UserSchema.statics.authenticate = function(email, password, callback) {
User.findOne({ email: email })
.exec(function (error, user) {
if (error) {
return callback(error);
} else if ( !user ) {
var err = new Error('User not found.');
err.status = 401;
return callback(err);
}
bcrypt.compare(password, user.password , function(error, result) {
if (result === true) {
return callback(null, user);
} else {
return callback();
}
})
});
}
// hash password before saving to database
UserSchema.pre('save', function(next) {
var user = this;
bcrypt.hash(user.password, 10, function(err, hash) {
if (err) {
return next(err);
}
user.password = hash;
next();
})
});
var User = mongoose.model('User', UserSchema);
module.exports = User;
the pug file :
div
form(method='post', action='/settings-user')
label ADD BIO
br
input(type='text', name='bio', placeholder='Enter something', required='')
input(type='submit', value='Add Bio')
</body>
If anyone could help,,,
thank you!

get valid password with bcrypt

I am using nodejs with sequelize to setup my database, and currently i am trying to hash and salt my password, the hashing i already did, but now when i tyr to login i want to compare the password send in req.body with the hashed one, so i did this:
router.post('/', function (req, res, next) {
console.log("hi");
if (JSON.stringify(req.body) == "{}") {
return res.status(400).json({ Error: "Login request body is empty" });
}
if (!req.body.username || !req.body.password) {
return res.status(400).json({ Error: "Missing fields for login" });
}
var password = User.validPassword(req.body.password);
// search a user to login
User.findOne({ where: { username: req.body.username, password: password } }) // searching a user with the same username and password sended in req.body
.then(function (user) {
if (!user) {
return res.status(400).json({ Error: "There is no user with those fields" }); // if there is no user with specific fields send
}
return res.status(400).json({ Error: "loged in!" }); // username and password match
}).catch(function (err) {
return res.status(200).json({ message: "server issues when trying to login!" }); // server problems
});
});
my model is like this:
"use strict";
var sequelize = require('./index');
var bcrypt = require('bcrypt-nodejs');
module.exports = function (sequelize, DataTypes) {
var User = sequelize.define("User", {
username: DataTypes.STRING,
email: DataTypes.STRING,
password: DataTypes.STRING
}, {
classMethods: {
generateHash: function (password) {
console.log("hi");
return bcrypt.hashSync(password, bcrypt.genSaltSync(8), null);
},
validPassword: function (password) {
return bcrypt.compareSync(password, this.password);
}
}
});
return User;
}
i don't get any response from
var password = User.validPassword(req.body.password);
i tried to console.log but stilm no response, when i try to go to router /login it just doesn0t give me any response it is loading all the time, any sugestion?
To start with, your route defines a post action to / not /login. You can use postman to try that out if you don't have a front-end yet.
The validPassword method should be called on an instance of the User model so you should have written this portion of the code like this
router.post('/', function (req, res, next) {
console.log("hi");
if (JSON.stringify(req.body) == "{}") {
return res.status(400).json({ Error: "Login request body is empty" });
}
if (!req.body.username || !req.body.password) {
return res.status(400).json({ Error: "Missing fields for login" });
}
// search a user to login
User.findOne({ where: { username: req.body.username } }) // searching a user with the same username and password sended in req.body
.then(function (user) {
if(user && user.validPassword(req.body.password)) {
return res.status(200).json({message: "login successful"});
} else {
return res.status(401).json({message: "Unauthorized"});
}
}).catch(function (err) {
return res.status(200).json({ message: "server issues when trying to login!" }); // server problems
});
});
Also in you catch method, the status code should be something that depicts internal server error like 500. 200 is inappropriate.
Soo seems like I could answer my own question, with the help of Femi Oladeji, basicly the problem was related to the fact that i want to acess the methods trough a instance and not a model.
So when I tried to acces it with the user instance, there was no method that treat instances, so made some changes on my model.
"use strict";
var sequelize = require('./index');
var bcrypt = require('bcrypt-nodejs');
module.exports = function (sequelize, DataTypes) {
var User = sequelize.define("User", {
username: DataTypes.STRING,
email: DataTypes.STRING,
password: DataTypes.STRING
}, {
classMethods: {
generateHash: function (password) {
return bcrypt.hashSync(password, bcrypt.genSaltSync(8), null);
},
},
instanceMethods: {
validPassword: function (password) {
console.log(password, this.password)
return bcrypt.compareSync(password, this.password);
}
}
});
return User;
}
and there is the instanceMethods, that can treat the instance :)

Nodejs/Express - Error: Can't set headers after they are sent

Pretty new to node/express. I'm checking to see if the user (via the username) already exists in the database that one wants to register to, giving an error if they do already exist.
When I use curl to try to set it off intentionally, I get the following error:
Error: Can't set headers after they are sent.
I know already that the first check I do to ensure that all the fields are filled in works correctly, and provides no issues with headers being set multiple times.
Any help would be greatly appreciated.
(My relevant code is below. If you need anything else, feel free to say so!)
router.post('/register', function(req, res, next) {
if(!req.body.username || !req.body.password){
return res.status(400).json({ message: 'Please fill out all fields.' });
}
User.count({ username: req.body.username}, function(err, count){
console.log(count);
if(count > 0) {
return res.status(400).json({message: 'This user already exists!' });
}
});
var user = new User();
user.username = req.body.username;
user.setPassword(req.body.password);
user.save(function(err) {
if(err) { return next(err); }
return res.json({ token: user.generateJWT()});
});
});
When you are returning inside User.count and user.save, you are returning only from inside the callbacks but not the entire method.
Its a good practice to send a response in just one place. At the end of the method. Before that evaluate your conditions and set the response code and response message in some variable. Which you can use to send the response as a final step.
Try this as a workaround for now:
router.post('/register', function(req, res, next)
{
if(!req.body.username || !req.body.password)
{
return res.status(400).json({ message: 'Please fill out all fields.' });
}
User.count({ username: req.body.username}, function(err, count)
{
console.log(count);
if(count > 0)
{
return res.status(400).json({message: 'This user already exists!' });
}
else
{
var user = new User();
user.username = req.body.username;
user.setPassword(req.body.password);
user.save(function(err)
{
if(err)
{
return next(err);
}
return res.json({ token: user.generateJWT()});
});
}
});
});
Put all your code in the callback function of User.count, otherwise the two part of code are executed
router.post('/register', function(req, res, next) {
if(!req.body.username || !req.body.password){
return res.status(400).json({ message: 'Please fill out all fields.' });
}
User.count({ username: req.body.username}, function(err, count){
console.log(count);
if(count > 0) {
return res.status(400).json({message: 'This user already exists!' });
}
var user = new User();
user.username = req.body.username;
user.setPassword(req.body.password);
user.save(function(err) {
if(err) { return next(err); }
return res.json({ token: user.generateJWT()});
});
});
});

Unexpected results with MongooseJS findOne()

Using Mongoose as an ODM with NodeJS, but not fully understanding how the error handling works. It works, but doesn't look right, and isn't in line with the documentation, so I'm worried that going down this road will haunt me later on.
For example, here is a basic signin route:
app.post('/signin', function(req, res){
var email = req.body.email;
var password = req.body.password;
mongoose.model('User').findOne({
email: email,
password: password
}, function(err, user){
if (err){
console.log('Database Error')
return res.json({error: 'Database Error'})
} else {
if (!user) {
console.log('User not found.');
return res.json({error: 'Email and/or password incorrect.'})
} else {
console.log('User ' + user.email + ' found. Logging in.');
res.json({
token: jwt.sign({}, 'top-secret', {subject: user}),
data: data[user]
})
}
}
})
})
I'm especially worried about:
if (err) {
//do something
} else {
if (!user){
//do something else
} else {
//log the user in
}
}
Haven't really used Mongo before today, but this feels like a lot of conditional error handling. Is there something that I'm not understanding properly here?
Was going to post as a comment but it was easier to paste this as an answer..
You can simplify the if-else nesting since you are returning at the end of each conditional, like so:
app.post('/signin', function (req, res) {
var email = req.body.email;
var password = req.body.password;
mongoose.model('User').findOne({
email: email,
password: password
}, function (err, user) {
if (err) {
console.log('Database Error');
return res.json({error: 'Database Error'});
}
if (!user) {
console.log('User not found.');
return res.json({error: 'Email and/or password incorrect.'});
}
console.log('User ' + user.email + ' found. Logging in.');
res.json({
token: jwt.sign({}, 'top-secret', {subject: user}),
data: data[user]
});
});
});

Categories