promise cancellable bluebird - javascript

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

Related

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.

Basic Authentication ComparePassword

I am currently working on a "Basic Authetntication" for Node JS. It should accept requests like the following:
POST http://localhost:8080/authenticate/
Authorization: Basic YWRtaW46MTIz
The AuthenticationService.js first reads the header and then passes the whole thing to the Userservice.js
AuthenticationService.js
async function basicAuth(req, res, next) {
// make authenticate path public
if (req.path === '/') {
return next();
}
if (!req.headers.authorization || req.headers.authorization.indexOf('Basic ') === -1) {
return res.status(401).json({ message: 'Missing Authorization Header' });
}
// verify auth credentials
const base64Credentials = req.headers.authorization.split(' ')[1];
const credentials = Buffer.from(base64Credentials, 'base64').toString('ascii');
const [username, password] = credentials.split(':');
console.log("AuthenticationService "+username+" "+password);
const user = await userService.authenticate({ username, password });
if (!user) {
return res.status(401).json({ message: 'Invalid Authentication Credentials' });
}
req.user=user
res.send(user)
next();
}
module.exports = {
basicAuth
}
The user service checks if the user is found and checks if the password is valid, only then the user object is sent back to the authentication service.
UserService.js
async function authenticate({ username, password }) {
let user = await User.findOne({userID: username})
user.comparePassword(password.toString(), function(err,isMatch) {
if (err){
console.log("error")
throw err;
}
if(isMatch)
{
console.log("Password correct")
}
if(!isMatch){
console.log("Password wrong")
}});
if(user){
return user;
}
else{
return null;
}
}
module.exports = {
authenticate
}
The .comparePassword-Method is inside the Usermodel.js:
UserSchema.methods.comparePassword = function(candidatePassword, cb) {
bcrypt.compare(candidatePassword, this.password, function(err, isMatch) {
if (err) return cb(err);
cb(null, isMatch);
});
};
const User = mongoose.model("User", UserSchema);
module.exports = User;
How can I send the boolean value of isMatch in the Userservice.js outside it's scope, so I can send the userobject back to the AuthenticationService.js depending on the correct password ? How can I improve that code ?
I erase the authenticate-method in Userservice.js and just call the crud-method. After that I call the compare-method and inside the if/else-block I pass a res.send.
function basicAuth(req, res, next) {
if (!req.headers.authorization || req.headers.authorization.indexOf('Basic ') === -1) {
return res.status(401).json({
message: 'Missing Authorization Header'
});
}
// verify auth credentials
const base64Credentials = req.headers.authorization.split(' ')[1];
const credentials = Buffer.from(base64Credentials, 'base64').toString('ascii');
const [username, password] = credentials.split(':');
console.log("AuthenticationService " + username + " " + password);
userService.findUserById(username, function(error, user) {
user.comparePassword(password.toString(), function(err, isMatch) {
if (err) {
console.log("Fehler")
throw err;
}
/*Passwort richtig*/
if (isMatch) {
res.send(user);
}
/*Passwort falsch*/
if (!isMatch) {
res.status(401).json({
message: 'Passwort und userID stimmen nicht überein.'
});
}
});
})
}

ValidationError: "g-recaptcha-response" is not allowed

I want to validate my registration page and i am using Joi validation every thing working fine except google recaptcha.
Here is code for form validation snippet,
var UserSchema = Joi.object().keys({
name: Joi.string().required(),
username: Joi.string().alphanum().min(4).max(30).required(),
email: Joi.string().email().required(),
mobile: Joi.string().required(),
password: Joi.string().regex(/^[a-zA-Z0-9]{8,30}$/),
confirmationPassword: Joi.any().valid(Joi.ref('password')).required(),
access_token: [Joi.string(), Joi.number()],
recaptcha : Joi.string()
});
Post request for registration page,
router.route('/register')
.get(isNotAuthenticated, (req, res) => {
res.render('register');
})
.post(async (req, res, next) => {
try {
const data = Joi.validate(req.body, UserSchema);
if (data.error) {
req.flash("error", "Enter Valid data");
console.log(data.error);
res.redirect('/register');
return;
}
const user = await User.findOne({ username: data.value.username });
if (user) {
req.flash('error', 'Username already taken');
res.redirect('/register');
return;
}
if (req.body.captcha === undefined ||
req.body.captcha === '' ||
req.body.captcha === null) {
res.redirect('/register');
return;
}
const secretKey = '';
const verifyUrl = `https://google.com/recaptcha/api/siteverify?secret=${secretKey}&response=${req.body.captcha}&remoteip=${req.connection.remoteAddress}`;
request(verifyUrl, async (err, response, body) => {
try{
body = JSON.parse(body);
console.log(body);
if (body.success !== undefined && !body.success) {
res.redirect('/register');
return;
} else {
const newUser = await new User(data.value);
console.log('newUser', newUser);
await newUser.save();
}
}catch(e){
throw e;
}
});
req.flash('success', 'Please check your email.');
res.redirect('/login');
} catch (error) {
next(error);
}
});
When i click on submit i got error message in console that { ValidationError: "g-recaptcha-response" is not allowed
You could access g-recaptcha-response using bracket notation. So in your Joi schema the property should look something like this:
["g-recaptcha-response"]: Joi.string().required();

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

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

Validating username with Mongoose schema

I'm trying to validate username before save it to mongodb. But instead saving or validation message i see the following message in terminal:
" if(user.path(username)){
TypeError: user.path is not a function"
What does it means?
I am newbie.
Here is my user.js
var User = require('models/user').User;
var HttpError = require('error').HttpError;
var async = require('async');
exports.get = function(req, res) {
res.render('login', { title: 'Login'});
};
exports.post = function(req, res, next) {
var username = req.body.username;
var password = req.body.password;
async.waterfall([
function(callback) {
User.findOne({username: username}, callback);
},
function(user, callback) {
if (user) {
if (user.checkPassword(password)) {
callback(null, user);
} else {
next(new HttpError(403, "wrong password"));
}
} else {
var user = new User({username: username, password: password});
if(user.path(username)){
callback(null, user);
user.save(function(err) {
console.log(err.message)
if (err)
return next(err);
callback(user);
});
}else{ next(new HttpError(403, "Incorrect username"));
};
}
}
], function(err, user){
if (err) return next(err);
req.session.user = user._id;
res.send({});
});
and here is my login.js
var crypto = require('crypto');
var mongoose = require('lib/mongoose'),
Schema = mongoose.Schema;
var schema = new Schema({
username: {
type: String,
unique: true,
required: true
},
hashedPassword: {
type: String,
required: true
},
salt: {
type: String,
required: true
},
created: {
type: Date,
default: Date.now
}
});
schema.path('username').validate(function (value, respond) {
return /[0-9]{6,15}[a-zA-Z]/.test(value, function(){
respond(false, 'this message gets to the validation error');
});
}, '{VALUE} is not a valid login - [0-9]{6,15}[a-zA-Z]')
schema.methods.encryptPassword = function(password) {
return crypto.createHmac('sha1', this.salt).update(password).digest('hex');
};
schema.virtual('password')
.set(function(password) {
this._plainPassword = password;
this.salt = Math.random() + '';
this.hashedPassword = this.encryptPassword(password);
})
.get(function() { return this._plainPassword; });
schema.methods.checkPassword = function(password) {
return this.encryptPassword(password) === this.hashedPassword;
};
schema.path('username').validate(function (value) {
return /[0-9]{6,15}[a-zA-Z]/.test(value);
}, 'Invalid color');
exports.User = mongoose.model('User', schema);
You don't need to call anything before saving to vaildate, mongoose will do it for you. Also don't call callback until you are done.
Also check if error occur before doing console.log(err.message) because err is null if now error happens.
So this should work:
} else {
var user = new User({username: username, password: password});
user.save(function(err) {
if (err) {
console.log(err.message);
return next(err);
}
callback(user);
});
}

Categories

Resources