Why isn't .save for Mongoose working in ReactJS? - javascript

I have a ReactJS and Redux connected to MongoDB, Mongoose.
I have a Mongoose Schema (user.js) set up like so:
var UserSchema = new Schema({
email: {
type: String,
lowercase: true,
unique: true,
required: true
},
})
And a API controller that receives the email string request, and then if nothing is entered in the text field, it sends a 422 error, and inside User.findOne, if the email already exists in the database, then it throws a 422 error and if not, does user.save to save it in the database.
"use strict";
const User = require('../models/user')
exports.register = function(req, res, next) {
const email = req.body.email;
console.log('ERROR 1')
if(!email) {
return res.status(422).send({ error: 'You must enter an email address.'})
console.log('ERROR 1')
}
User.findOne({ email: email }, function(err, existingUser) {
if(err) { return next(err); }
console.log('ERROR 2')
if(existingUser) {
return res.status(422).send({ error: 'That email address is already in use.'})
}
console.log('ERROR 3')
let user = new User({
email: email,
})
console.log('ERROR 4')
user.save(function(err, user) {
if(err) { return next(err); }
console.log('ERROR 5')
res.status(201).json({
user: user,
})
})
})
console.log('ERROR 6')
}
And I am making a POST request as such:
export function registerUser({ email }) {
return function(dispatch) {
axios.post(`${API_URL}/auth/register`, { email })
.then(response => {
console.log('THIS IS TESTING PURPOSE')
console.log(response)
dispatch({ type: AUTH_USER });
})
.catch((error) => {
errorHandler(dispatch, error.response, AUTH_ERROR)
});
}
}
I made several POST requests and all get successful status back from API with sever config: {'database': 'mongodb://localhost/practicedb',
'port': process.env.PORT || 3000}, yet the data never gets saved and database (practicedb) doesn't show up on Terminal.
Everything seem to be set up correctly but why the problem? Could I be missing something? Any insight or guidance would be really appreciated.
Thank you in advance!
Here are some logs and what's OPTIONS request that I never made:
Tried registering with same email again:

Correct if i'm wrong but your bare save method is not async. Save method return a promise. See http://mongoosejs.com/docs/promises.html
EDIT
user.save().then(function(doc) {
if (!doc) { next(new Error('Error while persisting!')); }
console.log('ERROR 5');
res.status(201).json({
user: doc
});
});
You can also achieve this with any promised library (Q, bluebird) or use ES6 Promise. Alternatively use async.
Example with Q. NOT TESTED:
"use strict";
const User = require('../models/user');
const Q = require('Q'); //add https://github.com/kriskowal/q
exports.register = function(req, res, next) {
const email = req.body.email;
console.log('ERROR 1')
if(!email) {
return res.status(422).send({ error: 'You must enter an email address.'})
console.log('ERROR 1')
}
var deferred = Q.defer();
User.findOne({ email: email }, function(err, existingUser) {
if(err) { return next(err); }
console.log('ERROR 2')
if(existingUser) {
return res.status(422).send({ error: 'That email address is already in use.'})
}
console.log('ERROR 3')
let user = new User({
email: email,
})
console.log('ERROR 4')
user.save(function(err, user) {
if(err) {
deferred.reject(err);
return next(err);
}
console.log('ERROR 5')
deferred.resolve(user); //
});
res.status(201).json({
user: deferred.promise,
})
})
console.log('ERROR 6')
}

Related

How to pass PassportJS message to React frontend in Express

I have been working with passport-local for user authentication and have been struggling in sending custom messages like "Username doesn't exist" or "Password doesn't match" from the server to the React frontend.
const user = await User.findOne({ email });
if (!user) {
console.log("No user`");
return done(null, false, {
message: "User with this email ID doesn't exist",
});
}
The current method I have is a very hacky one and just passes the passport.authenticate function inside an express function.
authRouter.post("/login", (req, res, next) => {
passport.authenticate("local", (err, user, info) => {
const error = err as Error;
if (error) {
return res.status(500).json({ message: error.message });
}
if (info) {
return res.status(400).json({ message: info.message });
}
req.logIn(user, (error) => {
if (error) {
return res.status(500).json({ message: error.message });
}
return res.json(user);
});
})(req, res, next);
});
Is there a better way to do this? I also tried using connect-flash with passport but wasn't able to quite get it working.
this is my code:
passport.use(new LocalStrategy(
const user = await User.findOne({ email });
if (!user) {
console.log("No user`");
return done(null, false, {
message: "User with this email ID doesn't exist",
});
}
authRouter.post("/login", passport.authenticate( "local", {
successReturnToOrRedirect: "Your_route",
failureRedirect: "/login",
failureMessage: true,
}))
))
and when you want send failure message to backend:
if (req.session.messages) {
//you can send req.session.message[0]
}
if you have session-express,you should delete session after sending req.session.message[0]:
delete req.session.messages
req.session.save()

App crashed after the wrong login credential attempt in node .js

Here is my code of login its work when the user enter the right credential but the app crashed when its enter the wrong credential by showing showing the error message "Internal server error" which is right beacause I wriiten in it catch code but what I want the app should not be crashed when the user enter the wrong credentials.
router.post(
"/login",
[
body("email", "you enter wrong email").isEmail(),
body("password", "password cannot be blank").exists(),
],
async (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
const { email, password } = req.body;
try {
let user = await User.findOne({ email });
if (!user) {
res.status(400).json({ error: "Please try to login with correct credentials" });
}
const passwordcompare = await bcrypt.compare(password, user.password);
if (!passwordcompare) {
res.status(400).json({ error: "Please Try to login with correct credential" });
}
const data = {
user: {
id: user.id,
},
};
const authtoken = jwt.sign(data, JWTSECRET);
res.json({ authtoken });
} catch (error) {
console.log(error.message);
res.status(500).send("Internal server error");
}
},
);
module.exports = router;
You're not returning after those res.status(400).json()s, so your program just continues on its merry way.
if (!user) {
res.status(400).json({error: "Please try to login with correct credentials"});
return; // add this
}
I think The problem in this line
const passwordcompare = await bcrypt.compare(password, user.password);
When password is undefined or wrong bcrypt.compare will throw an error and the catch block will catch it and return internal server error message
Try add return to res
if (!passwordcompare) {
return res.status(400).json({ error: "Please Try to login with correct credential" });
}
const data = {
user: {
id: user.id,
},
};
const authtoken = jwt.sign(data, JWTSECRET);
return res.json({ authtoken
});
You should add return statements on your error checks, otherwise, the function will keep executing and try to access user.password also if the user has not been found:
router.post(
"/login",
[
body("email", "you enter wrong email").isEmail(),
body("password", "password cannot be blank").exists(),
],
async (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
const { email, password } = req.body;
try {
let user = await User.findOne({ email });
if (!user) {
return res.status(400).json({ error: "Please try to login with correct credentials" });
}
const passwordcompare = await bcrypt.compare(password, user.password);
if (!passwordcompare) {
return res.status(400).json({ error: "Please Try to login with correct credential" });
}
const data = {
user: {
id: user.id,
},
};
const authtoken = jwt.sign(data, JWTSECRET);
res.json({ authtoken });
} catch (error) {
console.log(error.message);
res.status(500).send("Internal server error");
}
},
);
module.exports = router;
You do not return res.status() that's why your code crashed.

Compare passwords BcryptJS

So I'm trying to build a very basic user login. I'm trying to create a user, then login with those credentials and get back a JSON Web Token. Where I'm stuck is trying to compare the passwords then send a response.
Steps:
Create User:
enter email and password
salt/hash user password
store user into database
return success
Login
find user by request email value
if found compare passwords
passwords good send JSON Web Token
User Model
email:{
type: String,
required: true,
unique: true
},
password: {
type: String,
required: true
}
User Routes
var express = require('express');
var router = express.Router();
var jwt = require('jsonwebtoken');
var bcrypt = require('bcryptjs');
// Create User
...
bcrypt.genSalt(10, function(err, salt) {
bcrypt.hash("superSecret", salt, function(err, hash) {
user.password = hash;
user.save();
res.json({success: true, message: 'Create user successful'});
});
});
...
// Login
...
bcrypt.compare(req.body.password, 'superSecret', function(err, res) {
if(req.body.password != user.password){
res.json({success: false, message: 'passwords do not match'});
} else {
// Send JWT
}
});
So the two problems here is that, I can't send a response nor can I compare the password. Just completely stuck on this, any help would be greatly appreciated.
As described in the doc, you should use bcrypt.compare like that:
bcrypt.compare(req.body.password, user.password, function(err, res) {
if (err){
// handle error
}
if (res) {
// Send JWT
} else {
// response is OutgoingMessage object that server response http request
return response.json({success: false, message: 'passwords do not match'});
}
});
And here is a nice post about Password Authentication with Mongoose (Part 1): bcrypt
//required files
const express = require('express')
const router = express.Router();
//bcryptjs
const bcrypt = require('bcryptjs')
//User modal of mongoDB
const User = require('../../models/User')
//Post request for login
router.post('/', (req, res) => {
//email and password
const email = req.body.email
const password = req.body.password
//find user exist or not
User.findOne({ email })
.then(user => {
//if user not exist than return status 400
if (!user) return res.status(400).json({ msg: "User not exist" })
//if user exist than compare password
//password comes from the user
//user.password comes from the database
bcrypt.compare(password, user.password, (err, data) => {
//if error than throw error
if (err) throw err
//if both match than you can do anything
if (data) {
return res.status(200).json({ msg: "Login success" })
} else {
return res.status(401).json({ msg: "Invalid credencial" })
}
})
})
})
module.exports = router
If we you to use bcryptjs in browser(HTML) then you can add bcryptjs CDN to do this.
CDN - https://cdn.jsdelivr.net/npm/bcryptjs#2.4.3/dist/bcrypt.js
Example-
HTML- (Add above CDN in tag)
JS-
var bcrypt = dcodeIO.bcrypt;
/** One way, can't decrypt but can compare */
var salt = bcrypt.genSaltSync(10);
/** Encrypt password */
bcrypt.hash('anypassword', salt, (err, res) => {
console.log('hash', res)
hash = res
compare(hash)
});
/** Compare stored password with new encrypted password */
function compare(encrypted) {
bcrypt.compare('aboveusedpassword', encrypted, (err, res) => {
// res == true or res == false
console.log('Compared result', res, hash)
})
}
If you want to do same in Nodejs
/** Import lib like below and use same functions as written above */
var bcrypt = require('bcryptjs')
From what I can see your logic is correct.
If you are using mongoose I suggest you to use the pre 'save' hook.
User Schema
userSchema.pre('save', function(next) {
// only hash the password if it has been modified (or is new)
if (!this.isModified('password')) {
return next();
}
// generate a salt
return bcrypt.genSalt(10, function(error, salt) {
if (error) {
return next(error);
}
// hash the password using the new salt
return bcrypt.hash(this.password, salt, function(error, hash) {
if (error) {
return next(error);
}
// override the cleartext password with the hashed one
this.password = hash;
return next();
});
});
});
userSchema.methods.comparePassword = function(passw, cb) {
bcrypt.compare(passw, this.password, function(err, isMatch) {
if (err) {
return cb(err, false);
}
return cb(null, isMatch);
});
};
And in your routes:
Login
...
return user.comparePassword(password, function(error, isMatch) {
var payload = {
iat: Math.round(Date.now() / 1000),
exp: Math.round((Date.now() / 1000) + 30 * 24 * 60),
iss: 'Whatever the issuer is example: localhost:3000',
email: user.email
};
var token = jwt.encode(payload, 'secret');
if (isMatch && !error) {
// if user is found and password is right create a token
return res.json({
success: true,
token: `JWT ${token}`,
user: user,
msg: 'Authentication was succesful'
});
}
return next({code: 401, msg: 'Password is incorrect'});
});
});
Create user
// Pre hook will take care of password creation
return user.save()
.then(function(user) {
var payload = {
iat: Math.round(Date.now() / 1000),
exp: Math.round((Date.now() / 1000) + 30 * 24 * 60),
iss: 'Whatever the issuer is example: localhost:3000',
email: user.email
};
var token = jwt.encode(payload, 'secret');
return res.status(201).json({user, token: `JWT ${token}`, msg: 'User was succesfully created'});
})
.catch((err) => next(err));
bcrypt.compare(req.body.password, user.password, function(err, results){
if(err){
throw new Error(err)
}
if (results) {
return res.status(200).json({ msg: "Login success" })
} else {
return res.status(401).json({ msg: "Invalid credencial" })
}
})
const bcrypt = require("bcryptjs");
const salt = bcrypt.genSaltSync(10);
const hashPassword = (password) => bcrypt.hashSync(password, salt);
const comparePassword = (password, hashedPassword) =>
bcrypt.compareSync(password, hashedPassword);
bcrypt.compare(req.body.password, user.password)
.then(valid => {
if (!valid) {
return res.status(401).json({ message: 'Paire login/mot de passe incorrecte' });
}
res.status(200).json({
userId: user._id,
token:jwt.sign(
{userId: user._id},
process.env.ACCESS_TOKEN_SECRET_KEY,
{expiresIn:'24h'}
),
message: 'connected'
});
})
.catch(error => res.status(500).json({ error }));
enter code here

promise cancellable bluebird

I have signup function in expressjs
app.post('/api/signup', function(req, res) {
var username = req.body.username;
var password = req.body.password;
if (!username || !password) {
return res.status(400).end();
}
var promiseObject = userModel
.findOne({username: username})
.then(function(user) {
if (user) {
console.log(user);
res.json({success: false, message: 'User or Email already exists !'});
throw new promise.CancellationError()
} else {
var newUser = {username: username, password: bcrypt.hashSync(password)};
return userModel.create(newUser)
}
})
.cancellable()
.catch(promise.CancellationError, function(e) {
console.log(e.msg);
})
.then(function(user) {
console.log('User created successfully');
res.json({success: true});
})
.catch(function(err) {
console.log(err);
})
});
When i call function from postman, this show error : Unhandled Rejection Error, can't not set header after they are sent

Verification email with token in passport.js

I just looking for solution which makes verification email with token for my local autentification in passport.js
Is there some plugin or component for node which can make me verification easyer? Or I have to do it myself?
My controller
exports.postSignup = function(req, res, next) {
req.assert('email', 'Email is not valid').isEmail();
req.assert('password', 'Password must be at least 4 characters long').len(4);
req.assert('confirmPassword', 'Passwords do not match').equals(req.body.password);
var errors = req.validationErrors();
if (errors) {
req.flash('errors', errors);
return res.redirect('/signup');
}
var user = User.build({
email: req.body.email,
password: req.body.password,
});
User
.find({ where: { email: req.body.email } })
.then(function(existingUser){
if (existingUser) {
req.flash('errors', { msg: 'Account with that email address already exists.' });
return res.redirect('/signup');
}
user
.save()
.complete(function(err){
if (err) return next(err);
req.logIn(user, function(err){
if (err) return next(err);
res.redirect('/');
});
});
}).catch(function(err){
return next(err);
});
};
Thanks for any opinion!
Implementing this yourself is pretty straightforward.
The pseudocode:
//A user registers
//User is stored along with a random token string and a variable set to false
//User is sent a verification email
//Verification email has a link with the random token and a unique ID for that user
//Link goes to a route that takes the token as a parameter
//Match the user and the random token
//If they match - change a variable to verified
The package I use to generage the random string is:
https://www.npmjs.com/package/randomstring
Local signup strategy
passport.use('local-signup', new LocalStrategy({
// by default, local strategy uses username and password, we will override with email
usernameField: 'email',
passwordField: 'password',
passReqToCallback: true // allows us to pass back the entire request to the callback
},
function (req, email, password, done) {
// asynchronous
// User.findOne wont fire unless data is sent back
process.nextTick(function () {
// find a user whose email is the same as the forms email
// we are checking to see if the user trying to login already exists
User.findOne({'local.email': email}, function (err, user) {
// if there are any errors, return the error
if (err) {
return done(err);
}
// check to see if theres already a user with that email
if (user) {
console.log('that email exists');
return done(null, false, req.flash('signupMessage', email + ' is already in use. '));
} else {
User.findOne({'local.username': req.body.username}, function (err, user) {
if (user) {
console.log('That username exists');
return done(null, false, req.flash('signupMessage', 'That username is already taken.'));
}
if (req.body.password != req.body.confirm_password) {
console.log('Passwords do not match');
return done(null, false, req.flash('signupMessage', 'Your passwords do not match'));
}
else {
// create the user
var newUser = new User();
var permalink = req.body.username.toLowerCase().replace(' ', '').replace(/[^\w\s]/gi, '').trim();
var verification_token = randomstring.generate({
length: 64
});
newUser.local.email = email;
newUser.local.password = newUser.generateHash(password);
newUser.local.permalink = permalink;
//Verified will get turned to true when they verify email address
newUser.local.verified = false;
newUser.local.verify_token = verification_token;
try {
newUser.save(function (err) {
if (err) {
throw err;
} else {
VerifyEmail.sendverification(email, verification_token, permalink);
return done(null, newUser);
}
});
} catch (err) {
}
}
});
}
});
});
}));
I use a combination of /permalink/random-token for the verification URL
The route should look like this:
app.get('/verify/:permaink/:token', function (req, res) {
var permalink = req.params.permaink;
var token = req.params.token;
User.findOne({'local.permalink': permalink}, function (err, user) {
if (user.local.verify_token == token) {
console.log('that token is correct! Verify the user');
User.findOneAndUpdate({'local.permalink': permalink}, {'local.verified': true}, function (err, resp) {
console.log('The user has been verified!');
});
res.redirect('/login');
} else {
console.log('The token is wrong! Reject the user. token should be: ' + user.local.verify_token);
}
});
});

Categories

Resources