Validating username with Mongoose schema - javascript

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

Related

Mongoose, Bcrypt "Error: data and hash arguments required"

According to the error-trace the error occurs in the "validate" method, but as far as i see it my compare call is correct. I hope someone can explain to me why it happens anyways.
POST /user/register 500 23.312 ms - 2235
Error: data and hash arguments required
at Object.compare
at model.user_schema.methods.validate
The mongoose model:
const mongoose = require('mongoose');
const bcrypt = require('bcrypt');
const salty = 10;
const user_schema = new mongoose.Schema({
name: {
type: String,
required: true,
unique: false,
minlength: 3,
maxlength: 32
},
email: {
type: String,
required: true,
unique: true
},
password: {
type: String,
required: true
}
});
user_schema.pre('save', (next) => {
// if the password is not changed, there is no need to hash it again
if (!this.isModified('password')) return next();
// hash the user password
bcrypt.hash(this.password, salty, (err, hash) => {
if (err) return next(err);
this.password = hash;
return next();
});
});
user_schema.methods.validate = (claim, callback) => {
// compare the password to the existing hash from the database
bcrypt.compare(claim, this.password, (err, is_match) => {
if (err) return callback(err);
return callback(null, is_match);
});
}
module.exports = mongoose.model('user', user_schema);
The router with the create call:
router.post('/register', (req, res, next) => {
let new_user = {
name: req.body.name,
email: req.body.email,
password: req.body.password
};
user_model.create(new_user, (err, user) => {
if (err) return next(err);
res.send(user);
});
});

Node JS Express, Passport JS, and Android Device Authentication Issues

I am currently implementing a login page via Node JS Express and Passport using Passport's local strategy: http://www.passportjs.org/packages/passport-local/.
The database is using MongoDB.
The problem that is occurring is I am unable to login successfully (sometimes I can, but it is not consistent) on an android phone and android tablet with User A.
The response being returned is a 401 (unauthorized error).
I have verified that I can log in successfully consistently with User A from a desktop computer, and also verified I can login on iOS devices (iPhone and iPad).
Does anyone have any suggestions, and / or know what the issue is?
Below is the code that executes the authentication
app_api/config/passport.js
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
var mongoose = require('mongoose');
var User = mongoose.model('User');
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user);
});
});
passport.use(new LocalStrategy({
usernameField: 'email'
},
function(username, password, done) {
User.findOne({ email: username }, function (err, user) {
if (err) { return done(err); }
if (!user) {
return done(null, false, {
message: 'Incorrect username.'
});
}
if (!user.validPassword(password)) {
return done(null, false, {
message: 'Incorrect password.'
});
}
return done(null, user);
});
}
));
app_api/controllers/authentication.js
var passport = require('passport');
var mongoose = require('mongoose');
var User = mongoose.model('User');
var sendJSONresponse = function(res, status, content) {
res.status(status);
res.json(content);
};
module.exports.test = function(req, res) {
sendJSONresponse(res, 200, { 'status' : 'success' });
};
module.exports.register = function(req, res) {
if(!req.body.name || !req.body.email || !req.body.password) {
sendJSONresponse(res, 400, {
"message": "All fields required"
});
return;
}
var user = new User();
user.name = req.body.name;
user.email = req.body.email;
user.setPassword(req.body.password);
user.setShowResume(false);
user.save(function(err) {
var token;
if (err) {
sendJSONresponse(res, 404, err);
} else {
token = user.generateJwt();
sendJSONresponse(res, 200, {
'token' : token
});
}
});
};
module.exports.login = function(req, res) {
if(!req.body.email || !req.body.password) {
sendJSONresponse(res, 400, {
'message': 'All fields required'
});
return;
}
passport.authenticate('local', function(err, user, info){
var token, showResume;
if (err) {
sendJSONresponse(res, 404, err);
return;
}
if(user){
token = user.generateJwt();
showResume = user.showResume;
sendJSONresponse(res, 200, {
'token' : token,
'showresume': showResume
});
} else {
sendJSONresponse(res, 401, info);
}
})(req, res);
};
module.exports.logout = function(req, res) {
req.logout();
res.redirect('/');
};
Below is the controller app_server/controllers/index.js and the function logincontinue which calls the app_api/controllers/authentication.js file and its function login:
app_server/controllers/index.js
var request = require('request');
var apiOptions = {
server : "http://localhost:3000"
};
if (process.env.NODE_ENV === 'production') {
apiOptions.server = "https://siteDomain.com";
}
module.exports.home = function(req, res) {
renderLoginPage(req, res);
};
/* Other routes / functions ... */
module.exports.logincontinue = function(req, res) {
var path = '/api/authentication/login', sess;
//var connectSid = res.req.cookies['connect.sid'];
var requestOptions = {
url : apiOptions.server + path,
method : "POST",
json : { email: req.body.username, password: req.body.password }/*,
headers : { 'set-cookie': connectSid }*/
};
request(requestOptions, function(err, response, body) {
if(err) {
renderPage(req, res, 'siteDomain.com', 'loginerror');
} else if(response.statusCode === 200) {
switch(body.showresume) {
case true:
res.render('loginsuccessresume', { title: 'siteDomain.com', page: 'loginsuccessresume', showresume: true });
break;
default:
renderPage(req, res, 'siteDomain.com', 'loginsuccess');
break;
}
} else if(response.statusCode === 401) {
res.render('loginunauthorized', { title: 'siteDomain.com', page: 'loginunauthorized', errormessage: body.message });
} else {
renderPage(req, res, 'siteDomain.com', 'loginerror');
}
});
};
var renderPage = function(req, res, titleValue, pageValue) {
res.render(pageValue, { title: titleValue, page: pageValue });
};
var renderLoginPage = function(req, res) {
res.render('index', { title: 'siteDomain.com', page: 'login' });
};
As an update, this issue has been resolved.
The issue was related to case-sensitivity with respect to the username field.
Changing req.body.username to req.body.username.toLowerCase() resolved the issue within app_server/controllers/index.js and the function logincontinue.
Thank you.

I can't use my input of req.params.id into a function present in other file

I am building a REST API which allows us to create users and some coupons for businesses. For keeping things simple I only have defined a few routes.
My main file is coupon-api.js. I have defined my routes in this file and the function that are needed to perform in a different file namely coupons.js for coupon functions and users.js for user functions. My schema is defined in two separate files namely coupon.js and user.js.
The below code works perfectly the first time and returns an empty array.
app.get('/coupons' , coupons.getAllCoupons);
Next through this code I am able to create coupons using POSTMAN
app.post('/coupons' , coupons.createCoupon);
Now if I do
app.get('/coupons' , coupons.getAllCoupons);
I am able to get my newly created coupon with an ID defined.
The problem I am facing is that when I go to the following address
localhost:3000/coupons/(the id that I got of the new coupon)
it returns 500 internal server error. The main problem is that my ID which should be stored in req.params.id is not accessible in the file which defines all the functions needed to be performed i.e. coupons.js file.
All of my code is defined below.
Main file -- coupon-api.js
const express = require('express');
const path = require('path');
const logger = require('morgan');
const bodyParser = require('body-parser');
const mongoose = require('mongoose');
const config = require('./models/config');
const users = require('./controllers/users');
const coupons = require('./controllers/coupons');
var app = express();
mongoose.connect('localhost:5000');
if(app.get('env') === 'development') var dev = true;
// log if in dev mode
if(dev) app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended : false }));
//====================================
// More Middleware
//====================================
app.param('id' , function(req,res,next,id) {
if(!id.match(/^[0-9a-fA-F]{24}$/))
return res.status(400).send("invalid ID"); // using a regular expression to
//discard certain input
});
//=====================================
// Routes
//=====================================
app.get('/users' , users.getUsers);
app.get('/users/:id' , users.getUserById);
app.post('/users' , users.createUser);
app.delete('/users/:id' , users.deleteUserById);
app.put('/users/:id' , users.updateUser);
app.get('/coupons' , coupons.getAllCoupons);
app.get('/coupons/:id' , coupons.getCouponById);
app.post('/coupons' , coupons.createCoupon);
app.put('/coupons/:id' , coupons.updateCoupon);
app.delete('/coupons/:id' , coupons.deleteCouponById);
//Handle 404
app.use(function(req,res,next) {
var err = new Error('Not Found');
err.status = 404;
next(err);
});
//our defined error handler just for testing purposes
app.use(function(err,req,res,next) {
console.log('oops');
next(err);
});
//development error handler
if(dev) {
app.use(function(err,req,res,next) {
console.log(err);
res.status(err.status || 500).send();
});
}
//production error handler
app.use(function(err,req,res,next) {
res.status(err.status || 500).send();
});
var server = app.listen(config.port);
console.log("Listening at http://localhost:%s in %s mode" , server.address().port,app.get('env'));
module.exports =app;
Controller file defining functions of routes: coupons.js
const Coupon = require('../models/schemas/coupon');
module.exports.createCoupon = function(req,res,next) {
var newCoupon = new Coupon(req.body);
newCoupon.save(function(err,coupon) {
if(err) return next(err);
return res.status(200).send("OK");
});
}
module.exports.getAllCoupons = function(req,res,next) {
Coupon.find({} , function(err , coupons) {
if(err) return next(err);
return res.json(coupons);
});
}
module.exports.getCouponById = function(req,res,next) {
Coupon.findById(req.params.id , function(err, coupon) {
if(err) return next(err);
if(!coupon) return res.status(404).send("No coupon with that ID");
return res.json(coupon);
});
};
module.exports.updateCoupon = function(req,res,next) {
Coupon.findOneAndUpdate(req.params.id , req.body , {new:true} , function(err , coupon) {
if(err) return next(err);
if(!coupon) return res.status(404).send("No coupon with that ID");
return res.json(coupon);
});
}
module.exports.deleteCouponById = function(req,res,next) {
Coupon.findOneAndRemove(req.params.id , function(err,coupon) {
if(err) return next(err);
if(!coupon) return res.status(404).send("No coupon with that ID");
return res.status(200).send("OK");
});
}
module.exports.getActiveCoupons = function(req,res) {
Coupon.find( {
$and: [
{ startDate : { $lt : now } },
{ approvedDate: { $exists : true } },
{ $or: [
{ endDate : { $gt: now } },
{ endDate : { $exists : false } }
]}
]
} , function(err,coupons) {
if(err) return next(err);
return res.json(coupons);
});
}
module.exports.getUnapprovedCoupons = function(req, res, next) {
Coupon.find({approvedDate : {$exists : false }}, function(err, coupons) {
if(err) return next(err)
return res.json(coupons);
});
}
module.exports.approveCoupon = function(req, res, next) {
Coupon.finOneAndUpdate(req.params.id , { approvedDate : new Date() } , {new : true} , function(err, coupon) {
if(err) return next(err);
if(!coupon) return res.status(404).send("No coupon with that ID");
return res.json(coupon);
});
}
controller file for defining functions of user routes
const User = require('../models/schemas/user');
module.exports.createUser = function(req,res) {
var data = {
firstName : req.body.firstName,
lastName : req.body.lastName,
email : req.body.email,
password : req.body.password,
createdDate: new Date()
};
var newUser = new User(data);
//insert or store user using mongoose
newUser.save(function(err,user) {
return res.send('it worked');
});
}
module.exports.getUsers = function(req, res, next) {
User.find( {} , function(err , users) {
if(err) return next(err)
return res.json(users);
});
}
module.exports.getUserById = function(req,res,next) {
User.findById(req.params.id , function(err , user) {
if(err) return next(err);
if(!user) return res.status(404).send('user not found');
return res.json(user);
});
}
module.exports.updateUser = function(req,res,next) {
User.findOneAndUpdate(req.params.id , req.body , { new:true } , function(err, user) {
if(err) return next(err);
if(!user) return res.status(404).send("No user with that ID");
return res.json(user);
});
}
module.exports.deleteUserById = function(req,res,next) {
User.findOneAndRemove(req.params.id , function(err , user) {
if(err)
return next(err);
if(!user)
return res.status(404).send("No user with that ID");
return res.status(200).send('OK');
});
}
My schema code for coupons
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
var couponSchema = new Schema({
name: {type: String, required: true, trim: true},
url: {type: String, required: true, trim: true},
companyName: {type: String, required: true, trim: true},
startDate: {type: Date, default: Date.now, index: true},
endDate: {type: Date, index: true},
tags: [Number],
clicks: {type: [Date], default: []},
views: {type: [Date], default: []},
redeemed: {type: [Date], default: []},
postedBy: Schema.ObjectId, //ref: 'User', required: true},
approvedDate: Date
},
{
toObject: { getters: true },
//change name of mongoose default timestamps
timestamps: {
createdAt: 'createdDate',
updatedAt: 'updatedDate'
}
}
);
couponSchema.pre('save', function(callback) {
// ensure url starts with http://, https://, ftp://
if (this.url && !(/^((https?)|(ftp)):\/\/.+/.test(this.url)))
this.url = 'http://' + this.url;
// update startDate on approval
if (this.isModified('approvedDate') && this.approvedDate > this.startDate)
this.startDate = this.approvedDate;
callback();
});
var Coupon = mongoose.model('Coupon', couponSchema);
module.exports = Coupon;
My schema code for Users
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
var userSchema = new Schema({
firstName: {type: String, trim: true},
lastName: {type: String, trim: true},
classYear: Number,
email: {type: String, unique: true, sparse: true, trim: true},
phone: {type: String, unique: true, sparse: true},
phoneProvider: {type: String, trim: true},
interests: [Number],
isAdmin: {type: Boolean, index: true},
isSuperAdmin: {type: Boolean, index: true},
hash: String,
companyName: {type: String, trim: true},
token: String,
},
{
toObject: { getters: true },
timestamps: {
createdAt: 'createdDate',
updatedAt: 'updatedDate'
},
}
);
//hooks defined now
// hash if admin, ensure phone and provider if not
userSchema.pre('save', function(callback) {
if (this.isAdmin || this.isSuperAdmin) {
if (!this.email)
return callback(new Error('Missing email'));
if (!this.hash)
return callback(new Error('Missing password'));
if (!this.companyName)
return callback(new Error('Missing companyName'));
//TODO hash
}
else {
if (!this.phone)
return callback(new Error('Missing phone'));
if (!this.phoneProvider)
return callback(new Error('Missing phoneProvider'));
}
// validate phone
if (this.phone) {
if (typeof this.phone !== 'string')
return callback(new Error('Invalid phone'));
var phone = '';
for (var i = 0; i < this.phone.length; i++) {
if (!isNaN(this.phone[i]))
phone += this.phone[i];
}
if (phone.length !== 10)
return callback(new Error('Invalid phone'));
this.phone = phone;
}
callback();
});
// create full name
userSchema.virtual('name').get(function() {
var name = "";
if (this.firstName) {
name = this.firstName;
if (this.lastName) name += ' ' + this.lastName;
} else if (this.lastName) name = this.lastName;
return name;
});
// methods for validating password
userSchema.methods.comparePassword = function(pw, callback) {
bcrypt.compare(pw, this.hash, function(err, isMatch) {
if (err) return callback(err);
callback(null, isMatch);
});
};
var User = mongoose.model('User', userSchema);
module.exports = User;
I think the problem is in your middleware app.param(). Your should call next in order for the program flow to continue to your routes which contains param called id. You can try following.
app.param('id' , function(req, res, next, id) {
if(!id.match(/^[0-9a-fA-F]{24}$/)){
return res.status(400).send("invalid ID");
} else {
next();
}
});

How to handle dependencies with multiple routes in node express

I'm trying to implement authentication into my node-express-mongod app. To start with, I made an app.post in my server.js but having trouble getting result back using this curl:
curl -X POST -H "Content-Type: application/json" -d '{"username": "joe", "password": "passwordtest"}' http://localhost:8080/users
What I get is:
{"message":"Internal server error"}
My current server.js looks like this:
var express = require('express');
var bodyParser = require('body-parser');
var jsonParser = bodyParser.json();
var mongoose = require('mongoose');
var config = require('./config');
var Item = require('./models/item');
var User = require('./models/user-model');
// Mongoose Connect
var app = express();
app.use(bodyParser.urlencoded({
extended: true
}));
app.use(bodyParser.json());
app.use(express.static('public'));
var runServer = function(callback) {
mongoose.connect(config.DATABASE_URL, function(err) {
if (err && callback) {
return callback(err);
}
console.log("Connected to mongodb!");
app.listen(config.PORT, function() {
console.log('Listening on localhost:' + config.PORT);
if (callback) {
callback();
}
});
});
};
if (require.main === module) {
runServer(function(err) {
if (err) {
console.error(err);
}
});
};
// Route for passport test
app.post('/users', jsonParser, function(req, res) {
if (!req.body) {
return res.status(400).json({
message: "No request body"
});
}
if (!('username' in req.body)) {
return res.status(422).json({
message: 'Missing field: username'
});
}
var username = req.body.username;
if (typeof username !== 'string') {
return res.status(422).json({
message: 'Incorrect field type: username'
});
}
username = username.trim();
if (username === '') {
return res.status(422).json({
message: 'Incorrect field length: username'
});
}
if (!('password' in req.body)) {
return res.status(422).json({
message: 'Missing field: password'
});
}
var password = req.body.password;
if (typeof password !== 'string') {
return res.status(422).json({
message: 'Incorrect field type: password'
});
}
password = password.trim();
if (password === '') {
return res.status(422).json({
message: 'Incorrect field length: password'
});
}
var user = new User({
username: username,
password: password
});
user.save(function(err) {
if (err) {
return res.status(500).json({
message: 'Internal server error'
});
}
return res.status(201).json({});
});
});
// routes for crud operations
app.get('/items', function(req, res) {
Item.find(function(err, items) {
if (err) {
return res.status(500).json({
message: 'Internal Server Error'
});
}
res.json(items);
});
});
app.post('/items', jsonParser, function(req, res) {
console.log("req:", req.body);
if (!req.body) {
return res.sendStatus(400);
}
else {
var favoritesList = req.body;
res.status(201).json(favoritesList);
}
console.log("res:", res.status);
// console.log("name", name);
Item.create({
name: req.body.name,
wineResults: req.body.wineResults
},
function(err, item) {
if (err) {
message: err
}
else {
message: "done"
}
});
});
app.delete('/items/:name', function(req, res) {
Item.findOneAndRemove(req.params.name, function(err, items) {
if (err) {
return res.status(404).json({
message: 'Internal Server Error'
});
}
Item.remove({
name: req.body.name,
wineResults: req.body.wineResults
},
function(err, item) {
if (err) {
message: err
}
else {
message: "done"
}
});
});
});
app.put('/items/:id', function(req, res) {
var data = req.body; // data should be validated
Item.findOneAndUpdate({ name: req.params.id }
, { $set: { name: data.name }}
, { returnOriginal: false }
, function (err, items) {
if (err) {
return res.status(500).json({
message: 'Internal Server Error.'
});
}
return res.json(items);
}
);
});
app.use('*', function(req, res) {
res.status(404).json({
message: 'Not Found'
});
});
exports.app = app;
exports.runServer = runServer;
This is user-model.js:
var mongoose = require('mongoose');
var UserSchema = new mongoose.Schema({
username: {
type: String,
required: true,
unique: true
},
password: {
type: String,
required: true
}
});
var User = mongoose.model('User', UserSchema);
module.exports = User;
I'm just learning about authentication and node and just not sure what other steps I can take to troubleshoot this. Any help is appreciated. Thanks.

My API Route is not working; NodeJS

I am working on an authentication API system using NodeJS. The /Signup API endpoint is working fine, but the /authenticate is not. Everytime I call the /authenticate endpoint, I get the error message: 'Could not authenticate user' even when a valid user is provided;
Below is my code. Please tell me what I am doing wrong here
var express = require("express");
var mongoose = require("mongoose");
var User = require("../models/user");
module.exports = function (router) {
router.post('/signup', function (req,res) {
var user = new User();
user.local.username = req.body.username;
user.local.email = req.body.email;
user.local.password = req.body.password;
if (req.body.username == null || req.body.username == '' || req.body.email == null || req.body.email == '' || req.body.password == null || req.body.password == '') {
res.json({success:false, message:'Ensure username, email and password were provided'});
} else {
user.save(function (err, data) {
if (err) res.json({success:false, message:'Username or Email already exists!'});
// console.log(err.errors)
res.json({success:true, message:'New user created', data:data});
console.log(data)
});
}
})
router.post('/authenticate', function (req,res) {
User.findOne({username: req.body.username}).exec(function (err,user) {
if(err)
return res.send(err);
if (!user) {
res.json({success:false, message: 'Could not authenticate user'});
} else if(user){
var validPassword = user.comparePassword(req.body.password)
if (!validPassword) {
res.json({success:false, message: 'Could not authenticate password'});
} else{
res.json({success:true, message: 'User authenticated'});
}
}
});
});
}
EDIT
User Model:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var bcrypt = require('bcrypt-nodejs');
// define the schema for our user model
var userSchema = new Schema({
local : {
username : {type:String,unique:true,required:true, lowercase:true},
email : {type:String,unique:true,required:true, lowercase:true},
password : String
},
created_at : {type:Date, default:Date.now},
updated_at : {type:Date, default:Date.now}
});
userSchema.pre('save', function(next){
var user = this;
var now = new Date();
user.updated_at = now;
if(!user.created_at){
user.created_at = now
}
bcrypt.hash(user.local.password, null, null, function (err, hash) {
if(err) return next(err)
user.local.password = hash;
next(); })
});
// checking if password is valid
userSchema.methods.comparePassword = function(password) {
return bcrypt.compareSync(password, this.local.password); };
// create the model for users and expose it to our app
module.exports = mongoose.model('User', userSchema);
just saw the bug its you have your username inside the local.
router.post('/authenticate', function (req,res) {
User.findOne({'local.username': req.body.username}).exec(function (err,user) {
if(err)
return res.send(err);
else{
}
});

Categories

Resources