var mongoose = require('mongoose');
var bcrypt = require('bcrypt-nodejs');
var Schema = mongoose.Schema;
var UserSchema = new Schema({
username:{type: String,lowercase: true, required: true,unique:true},
password:{type:String, required:true},
email:{type:String,required:true,lowercase:true,unique:true},
});
// mongoose middleware
UserSchema.pre('save',(req,res,next)=>{
var user = this;
bcrypt.hash(user.password,null,null,(err,hash)=>{
if(err) return next(err);
user.password = hash;
next();
});
});
module.exports = mongoose.model('User',UserSchema);
below the error i can't find out the error
POST /users 200 58.468 ms - 14
/var/www/html/meanstacktutorial/app/models/user.js:17
next();
^
ReferenceError: next is not defined
at bcrypt.hash (/var/www/html/meanstacktutorial/app/models/user.js:17:9)
at /var/www/html/meanstacktutorial/node_modules/bcrypt-nodejs/bCrypt.js:631:3
at _combinedTickCallback (internal/process/next_tick.js:131:7)
at process._tickCallback (internal/process/next_tick.js:180:9)
[nodemon] app crashed - waiting for file changes before starting...
when i make password to encrypt then it gets some error like "next() is not defined " why i don't know exactly ..i have been trying to slove the problem but i couldn't please help me out thank you
The function passed to your pre-save hook should only take next as an argument.
You have also passed in req and res.
Try this instead:
UserSchema.pre('save', (next) => {
var user = this;
bcrypt.hash(user.password, null, null, (err, hash) => {
if(err) return next(err);
user.password = hash;
next();
});
});
I hope this helps.
Related
at the moment I'm following a tutorial to push myself in the topics node + react.
The link to the repo is https://bitbucket.org/grtn/cloudstruct/src/develop/
When I make a post request to /api/users/register i get the following error in the console and i can't figure out why.
/Users/****/Dev/cloudstruct/routes/api/users.js:38
if(err) throw err;
^
Error: Illegal arguments: undefined, string
at _async (/Users/****/Dev/cloudstruct/node_modules/bcryptjs/dist/bcrypt.js:214:46)
at Object.bcrypt.hash (/Users/****/Dev/cloudstruct/node_modules/bcryptjs/dist/bcrypt.js:220:13)
at bcrypt.genSalt (/Users/****/Dev/cloudstruct/routes/api/users.js:37:28)
at Immediate.<anonymous> (/Users/****/Dev/cloudstruct/node_modules/bcryptjs/dist/bcrypt.js:153:21)
at runCallback (timers.js:756:18)
at tryOnImmediate (timers.js:717:5)
at processImmediate [as _immediateCallback] (timers.js:697:5)
[nodemon] app crashed - waiting for file changes before starting...
The Usermodel looks like this:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
// Create Schema
const UserSchema = new Schema({
name:{
type: String,
required: true
},
email:{
type: String,
required: true
},
password:{
type: String,
required: true
},
avatar:{
type: String
},
date:{
type: Date,
default: Date.now
}
});
module.exports = User = mongoose.model('users', UserSchema);
And my routing:
const express = require ('express');
const router = express.Router();
const gravatar = require('gravatar');
const bcrypt = require('bcryptjs');
// Load User model
const User = require ('../../models/User')
//#route GET api/users/test
//desc Tests post route
//#access Public
router.get('/test', (req, res) => res.json({msg: '<h1>Hello World</h1>'}));
//#route POST api/users/register
//desc Register User
//#access Public
router.post('/register', (req, res) =>{
User.findOne({ email: req.body.email })
.then(user => {
if(user) {
return res.status(400).json({email: 'Email already exists'});
} else {
const avatar = gravatar.url(req.body.email, {
s: '200',
r: 'pg',
d: 'mm'
});
const newUser = new User({
name: req.body.name,
email: req.body.email,
avatar: avatar,
password: req.body.password
});
bcrypt.genSalt(10, (err, salt) => {
bcrypt.hash(newUser.password, salt, (err, hash) => {
if(err) throw err;
newUser.password = hash;
newUser
.save()
.then(user => res.json(user))
.catch(err => console.log(err));
})
});
}
});
});
module.exports = router;
Thanks for your help!
Your newUser.password is undefined. I am afraid that we can access mongoose docs like this. The solution in this case is to use req.body.password in .hash()
For reference: If you want to access the key value of a mongoose doc, your have to parse the doc into JSON.
The correct way to hash passwords with Mogoose is to use presave middleware. It will give you a guarantee that user password will always be hashed regardless of the place where you want to create a user record. Also, it's better for architecture: hashing will be made in the data layer instead of routing.
Here is a good example. Middleware is described in the step 2.
Let's say we have two file, user.js users.js in user.js we have. Why can we do module.exports.. we can use in it diff .js file? what does "#returns Promise If callback has been omitted" means it is from the bcrypt.genSalt function?
I also have a github repo, so please take a look if you have a bit of time. after cloning it
stuck in terminal
result { error: null,
value:
{ email: 'max#mail.com',
username: 'max',
password: '1234',
confirmationPassword: '1234' },
then: [Function: then],
catch: [Function: catch] }
hash undefined
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const bcrypt = require('bcryptjs');
const userSchema = new Schema({
email: String,
username: String,
password: String
});
const User = mongoose.model('user', userSchema);
module.exports = User;
module.exports.hashPassword = (password) => {
return hash = bcrypt.genSalt(10, function(err, salt) {
bcrypt.hash(password, salt, function(err, hash) {
});
});
};
in users.js we have have
const express = require('express');
const router = express.Router();
const Joi = require('joi');
const User = require('../models/user');
const userSchema = Joi.object().keys({
email:Joi.string().email().required(),
username:Joi.string().required(),
password:Joi.string().regex(/^[a-zA-Z0-9]{3,15}$/).required(),
confirmationPassword:Joi.any().valid(Joi.ref('password')).required()
});
router.route('/register')
.get((req, res) => {
res.render('register');
})
.post(async (req, res, next) => {
try{
const result = Joi.validate(req.body,userSchema);
console.log('result',result);
if(result.error) {
req.flash('error', 'Data is not valid, please try again');
res.redirect('/users/register');
return;
//console.log('result',result);
}
// checking if email is already taken
const user = await User.findOne({'email':result.value.email });
if (user){
req.flash('error','Email is already in use');
res.redirect('/users/register');
return;
}
// console.log('hash',hash);
// Hash the password
const hash = await User.hashPassword(result.value.password);
console.log('hash',hash);
} catch(error) {
next(error);
}
});
module.exports = router;
based the example given by bcrypt
var bcrypt = require('bcryptjs');
bcrypt.genSalt(10, function(err, salt) {
bcrypt.hash("B4c0/\/", salt, function(err, hash) {
// Store hash in your password DB.
});
});
adding pic for mongo db
I think the problem lies in this line:
const hash = await User.hashPassword(result.value.password);
This implies that User.hashPassword(result.value.password) should be returning a promise (but it returns a reference to the wrong promise).
module.exports.hashPassword = (password) => {
return hash = bcrypt.genSalt(10, function (err, salt) {
bcrypt.hash(password, salt, function (err, hash) {});
});
};
Perhaps modifying the above to return a promise may help.. Like so:
module.exports.hashPassword = (password) => {
var salt = await bcrypt.genSalt(10);
return bcrypt.hash(password, salt);
};
To answer your question about #returns Promise If callback has been omitted:
Bcrypt methods are asynchronous. Which means they return immediately and process in the background. When the result is available, the function makes this available to the calling code either via a callback function or a promise.
Consider the following API for genSalt from the docs:
genSalt(rounds, minor, cb)
rounds - [OPTIONAL] - the cost of processing the data. (default - 10)
minor - [OPTIONAL] - minor version of bcrypt to use. (default - b)
cb - [OPTIONAL] - a callback to be fired once the salt has been generated. uses eio making it asynchronous. If cb is not specified, a Promise is returned if Promise support is available.
err - First parameter to the callback detailing any errors.
salt - Second parameter to the callback providing the generated salt.
What that says is genSalt can take three arguments: genSalt(rounds, minor, cb)
Sample using callbacks
If the calling code wants the result via a callback, it can pass a function which looks like function(err, salt){} as the cb parameter.
bcrypt.genSalt(rounds, minor, function(err, salt){
if(err){
//Handle error
return;
}
// Salt is available here
console.log(salt);
});
Sample using promises
If the cb parameter is not passed (null or undefined) the function returns a Promise instead.
var promise = bcrypt.genSalt(rounds, minor);
promise
.then(function(salt){
// Salt is available here
console.log(salt);
})
.catch(function(err){
// Handle error
});
this is my mean code.
const express = require('express');
const router = express.Router();
const passport = require('passport');
const jet = require('jsonwebtoken');
const Contact = require('../models/contacts');
// retrieving Data
router.get('/contacts',(req,res,next)=>{
// res.send('Retriving the contact list');
console.log('contacts page');
Contact.find(function(err, contacts){
res.json(contacts);
})
});
// to add the content
router.post('/contact',(req, res, next)=>{
// logic to add contact
let newContact = new Contact({
first_name: req.body.first_name,
last_name: req.body.last_name,
email_id: req.body.email_id,
password: req.body.password
});
Contact.addRegistry((err, contacts)=> {
if(err) {
res.json({msg:'faild to add register'});
}
else{
res.json({msg:'registry added sucessfully'});
}
});
});
// to delete the content
router.delete('/contact/:id',(req, res, next) =>{
// logic to delete contact
Contact.remove({_id:req.params.id}, function(err, result){
if(err){
res.json(err);
}
else {
res.json(result);
}
});
})
module.exports = router;
the above file is route.js.
the below code is from contact.js
// Database code.
var express = require('express');
var app = express();
var mongoose = require('mongoose');
var bcrypt = require('bcryptjs');
// database schaema
var ContactSchema = new mongoose.Schema({
first_name: String,
last_name: String,
id: String,
location: String,
profile_picture_url: String,
email_id: String,
phone: String,
job_title: String,
company: String,
education: String,
password: String,
savedjobslist: {
title: [],
subtitle: []
},
appliedjobslist: {
title: [],
subtitle: []
},
failedjobslist: {
title: [],
subtitle: []
}
});
const Contact = module.exports = mongoose.model('Contact', ContactSchema);
module.exports.getUserById = function(id,callback) {
Contact.findById(id,callback);
}
module.exports.getUserById = function(username,callback) {
const query = {username: username}
Contact.findOne(query,callback);
}
module.exports.addRegistry = function(newContact,callback) {
bcrypt.genSalt(10, (err,salt) => {
bcrypt.hash(newContact,salt, (err,hash) => {
if (err) {
console.log(err);
}
newContact.password = hash;
newContact.save(callback);
});
});
}
I'm trying to post the data from postman it is shoing the error as
"there was error connecting to http://localhost:3000/api/contact"
and in the command prompt it is showing the error as
Server started at port3000 connected to mongos database at 27017
Error: Illegal arguments: function, string
at _async (D:\project-1\back-end\node_modules\bcryptjs\dist\bcrypt.js:214:46 )
at Object.bcrypt.hash (D:\project-1\back-end\node_modules\bcryptjs\dist\bcry pt.js:220:13)
at bcrypt.genSalt (D:\project-1\back-end\models\contacts.js:49:16)
at Immediate._onImmediate (D:\project-1\back-end\node_modules\bcryptjs\dist\ bcrypt.js:153:21)
at runCallback (timers.js:794:20)
at tryOnImmediate (timers.js:752:5)
at processImmediate [as _immediateCallback] (timers.js:729:5) D:\project-1\back-end\models\contacts.js:54
newContact.save(callback);
^
TypeError: newContact.save is not a function
at bcrypt.hash (D:\project-1\back-end\models\contacts.js:54:23)
at runCallback (timers.js:794:20)
at tryOnImmediate (timers.js:752:5)
at processImmediate [as _immediateCallback] (timers.js:729:5) [nodemon] app crashed - waiting for file changes before starting...
newContact.save(callback);
^
TypeError: newContact.save is not a function.
i don't know why this error is coming.
You have an issue here:
bcrypt for generating is throwing an error because of wrong parameters. You can't pass object (newContact) to bcrypt.
Try to generate a hash using the following code:
const salt = bcrypt.genSaltSync(10);
const hashedPassword = bcrypt.hashSync(password, salt);
You can use pre save function of mangoose to generate hashedPassword while storing this. Personally, I don't prefer as this adds new check everytime you save the object
find() takes query as an argument to search all pass {}
Contact.find({},function(err, contacts){
res.json(contacts);
})
Contact.addRegistry waits newContact as first parameter, but you do not pass it on your route.js
I think you want to do something like this
ContactSchema.pre('save', function(next) {
const user = this;
// generate a salt
bcrypt.genSalt(10, function(err, salt) {
if (err) return next(err);
// hash your password
bcrypt.hash(user.password, salt, function(err, hash) {
if (err) return next(err);
// store hash on the password field
user.password = hash;
next();
});
});
});
I'm trying to write an endpoint for an API that will return all orders for a given user. My issue is that when I try to query the database using mongoose's findById function, the 'user' object is undefined in the callback function and I can't query the orders subdoc. To add to the confusion, I can get it to work if I don't use a callback function, but then I don't have proper error handling.
var mongoose = require('mongoose');
var router = express.Router();
var order_model = require('../models/order');
var user_model = require('../models/user');
router.get('/:userid/order/', function (req, res) {
// This works???
var u = user_model.findById(req.params.userid);
res.json(u.orders);
});
The following code throws the error "TypeError: Cannot read property 'orders' of undefined".
var mongoose = require('mongoose');
var router = express.Router();
var order_model = require('../models/order');
var user_model = require('../models/user');
router.get('/:userid/order/', function (req, res) {
// This throws an error.
user_model.findById(req.params.userid).then(function (err, user) {
if (err) {
res.send(err);
}
res.json(user.orders);
});
});
user.js
var mongoose = require('mongoose');
var ordersSchema = require('./order').schema;
var userSchema = new mongoose.Schema({
name: String,
email: String,
showroom: String,
orders: [ordersSchema]
});
module.exports = mongoose.model('User', userSchema);
order.js
var mongoose = require('mongoose');
var lineItemsSchema = require('./lineitem').schema;
var ordersSchema = new mongoose.Schema({
trackingNumber: Number,
lineItems: [lineItemsSchema]
});
module.exports = mongoose.model('Order', ordersSchema);
Any help / explanation of this behavior would be appreciated. Thanks!
The first parameter of the then callback is user, not err.
Either use a traditional callback:
user_model.findById(req.params.userid, function (err, user) {
if (err) {
res.send(err);
return; // Need to return here to not continue with non-error case
}
res.json(user.orders);
});
Or chain a call to catch on the promise to separately handle errors:
user_model.findById(req.params.userid).then(function (user) {
res.json(user.orders);
}).catch(function(err) {
res.send(err);
});
I usually query like this, it work perfect :
user_model.find(_id : req.params.userid)
.exec((err, user) => {
if(err){
//handle error
}
return res.status(200).json(user.orders);
})
});
My problem is that I'm getting the error "Error: Failed to serialize user into session". I'm confused, because I've set a serializeUser function, but it doesn't appear to be called (my console.log isn't being printed).
This is while I'm following the feathers passport tutorial: http://feathersjs.com/learn/authorization/
Note: my suspicion is that feathers-passport uses a different "passport" object than my own library. Unfortunately I have no idea how I would rememdy such an issue. It seems to me it's just horrendous design by Passport to not work by passing around instances, and instead attaching things to itsself directly.
I'm setting up passport for serialization and authentication using the following:
var LocalStrategy = require('passport-local').Strategy;
function GetPassport(userService, Passport) {
console.log('passport has been prepared.\n');
Passport.serializeUser(function(user, done) {
console.log('user: ', user);
done(null, user._id);
});
Passport.deserializeUser(function(id, done) {
userService.get(id, {}, done);
});
Passport.use(new LocalStrategy(function(username, password, done) {
userService.authenticate(username, password, done);
}));
return Passport;
}
module.exports = GetPassport;
Then I'm using:
var userService = UserService(config.db);
var passport = GetPassport(userService);
app.post('/login', passport.authenticate('local'));
If you need more details here is UserService:
var MongoDB = require('feathers-mongodb');
var Crypto = require('crypto');
var UserService = function(database) {
return MongoDB({
db: database,
collection: '_users',
}).extend({
authenticate: function(username, password, callback) {
this.find({query: {username: username}}, function(error, users) {
if(error)
callback(error);
var user = users[0];
if(!user)
return callback(new Error('No User Found'));
if(user.password !== hash(password, user.salt))
return callback(new Error('Password Is Incorrect'));
//success, return the authenticated user
return callback(null, user);
});
},
setup: function() {
this.before({
create: function(hook, next) {
//Create the salt
var salt = Crypto.randomBytes(128).toString('base64');
hook.data.salt = salt;
hook.data.password = hash(hook.data.password, hook.data.salt);
next();
},
});
},
});
};
module.exports = UserService;
function hash(string, salt) {
var shasum = Crypto.createHash('sha256');
shasum.update(string + salt);
return shasum.digest('hex');
}
The error trace:
Error: Failed to serialize user into session
at pass (/Users/funk/Development/Projects/generic_rest_server/node_modules/feathers-passport/node_modules/passport/lib/authenticator.js:277:19)
at Authenticator.serializeUser (/Users/funk/Development/Projects/generic_rest_server/node_modules/feathers-passport/node_modules/passport/lib/authenticator.js:295:5)
at IncomingMessage.req.login.req.logIn (/Users/funk/Development/Projects/generic_rest_server/node_modules/passport/lib/http/request.js:48:29)
at Strategy.strategy.success (/Users/funk/Development/Projects/generic_rest_server/node_modules/passport/lib/middleware/authenticate.js:228:13)
at verified (/Users/funk/Development/Projects/generic_rest_server/node_modules/passport-local/lib/strategy.js:83:10)
at /Users/funk/Development/Projects/generic_rest_server/user-service.js:22:24
at /Users/funk/Development/Projects/generic_rest_server/node_modules/feathers-mongodb/node_modules/mongoskin/node_modules/mongodb/lib/mongodb/cursor.js:158:16
at commandHandler (/Users/funk/Development/Projects/generic_rest_server/node_modules/feathers-mongodb/node_modules/mongoskin/node_modules/mongodb/lib/mongodb/cursor.js:651:16)
at /Users/funk/Development/Projects/generic_rest_server/node_modules/feathers-mongodb/node_modules/mongoskin/node_modules/mongodb/lib/mongodb/db.js:1670:9
at Server.Base._callHandler (/Users/funk/Development/Projects/generic_rest_server/node_modules/feathers-mongodb/node_modules/mongoskin/node_modules/mongodb/lib/mongodb/connection/base.js:382:41)
The answer was in my note.
This should be helpful for anyone else who gets stuck following:
"http://feathersjs.com/learn/authorization/"
You must provide the FeathersPassport call with the passport option. If not, feathers-passport well use a different version of passport, than the one you add serializeUser to:
app.configure(FeathersPassport(function(result) {
// MongoStore needs the session function
var MongoStore = ConnectMongo(result.createSession);
result.secret = 'noymysecret';
result.store = new MongoStore({
db: config.db,
});
result.resave = false;
result.saveUninitialized = false;
//*HERE*//
result.passport = passport;
//**//
return result;
}));
I blame Passport inexplicably being a singleton, for not noticing this sooner.