I have a simple ExpressJS app and I'm trying to add user authentication to it using passport, passport-local and passport-local-mongoose, but after I save a user's data on db, mongoose fails to retrieve it, although I can see the data through mongo cli.
At the main express module I have:
// Passport session setup.
var User = require("./models/user");
passport.use(new LocalStrategy(User.authenticate()));
passport.serializeUser(User.serializeUser());
passport.deserializeUser(User.deserializeUser());
My User model module have:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var passportLocalMongoose = require('passport-local-mongoose');
// User Schema
var User = new Schema({
username: { type: String, required: true, unique: true },
password: { type: String, required: true}
});
User.plugin(passportLocalMongoose);
module.exports = mongoose.model('User', User);
At my routes module, I create a user with the following code:
router.post('/register', function(req, res) {
var u = req.body.username;
var p = req.body.password;
console.log('Creating account for ' + u + ':' + p);
// Store client on DB
var usr = new User({ username: u, password: p });
usr.save(function(err) {
if(err) {
console.log(err);
return res.render('register', {title: 'Register', error: err.userMessage});
} else {
console.log('user: ' + usr.username + " saved.");
passport.authenticate('local')(req, res, function () {
return res.redirect('/dashboard');
});
}
});
});
But when I try to authenticate a user with the passport.authenticate method, mongoose cannot find the user at the db, as I can see on the mongoose logs:
Login request # 1422120637815
Mongoose: users.findOne({ username: 'e#e.com' }) { fields: undefined }
But at mongo cli I can see the data:
> db.User.find()
{ "username" : "e#e.com", "password" : "123", "_id" : ObjectId("54c3d688fc71a4001db30612"), "__v" : 0 }
Mongoose is looking for a collection named 'users', but it looks like your data is stored in 'User'. The API for model() is mongoose#model(name, [schema], [collection], [skipInit]). so you can force the collection name by adding a third parameter e.g.
module.exports = mongoose.model('User', User, 'User');
EDIT1:
Try using the built-in register() method in mongoose-local
router.post('/register', function(req, res) {
var u = req.body.username;
var p = req.body.password;
console.log('Creating account for ' + u + ':' + p);
// Store client on DB
Account.register(new Account({ username: req.body.username }), req.body.password, function(err) {
if(err) {
console.log(err);
return res.render('register', {title: 'Register', error: err.userMessage});
} else {
passport.authenticate('local')(req, res, function () {
return res.redirect('/dashboard');
});
}
});
});
Related
How can i access for example username and put it in profile page ?
model/db.js
const mongoose = require('mongoose');
const stDB = mongoose.Schema({
username : {
type: String,
required: true
},
email : {
type: String,
required: true
},
password : {
type: String,
required: true
}
});
module.exports = mongoose.model('db', stDB);
views/profiles/instructor.hbs
<h5>I want access username from db and put it here!</h5>
index.js
const users = require('../model/db'); // db that username stored in it (model/db.js)
//instructor
router.get('/profiles/instructor', function (req, res, next) {
res.render('./profiles/instructor', {
title: 'Instructor'
});
});
router.post('/signup', function (req, res, next){
const newUser = new users({
username : req.body.username,
email : req.body.email,
password : req.body.password,
});
users.findOne({email : req.body.email}, (err, doc)=>{
if(err){
console.log('ERR while getting username =>' + err);
return ;
}
if(doc){
res.send('this email is already registered before!');
return ;
}
newUser.save((err, doc)=>{
if(err){
console.log('err' + err)
}else{
console.log(doc)
res.redirect('/login')
}
});
});
// etc.....
MongoDB inserts document without properties(look at the bottom of the post to see how my collection looks like).I'm using postman to test my db when I try to insert the following:
{
"username":"JohnDoe",
"password" : "123456"
}
I'm building a MEAN stack application.Also, I noticed if I set the properties to be required, postman tells me that it Failed to register user. That's why I comment it out, but even without it and getting a postive response I still get empty documents in my collection.
Postman tells me:
{
"success": true,
"msg": "User registered"
}
My user.js file
const bcrypt = require ('bcryptjs');
const config = require ('../config/database');
//UserSchema
const UserSchema = mongoose.Schema({
username: {
type: String,
//required: true
},
password: {
type: String,
//required: true
}
});
const User = module.exports = mongoose.model("User", UserSchema);
//To user function outside
module.exports.getUserById = function(id, callback){
User.findById(id,callback);
}
module.exports.getUserByUsername= function(username, callback){
const query = {username: username}
User.findOne (query,callback);
}
User.addUser = function (newUser, callback) {
bcrypt.genSalt(10, (err, salt) =>{
bcrypt.hash(newUser.password, salt, (err, hash) => {
newUser.password = hash;
newUser.save(callback);
});
});
}
My users.js file:
const express = require('express');
const router = express.Router();
const passport = require('passport');
const jwt = require('jsonwebtoken');
const User = require('../modules/user');
// Register
router.post('/register', (req, res, next) => {
let newUser = new User({
username: req.body.username,
password: req.body.password
});
User.addUser(newUser, (err, user) => {
if(err){
res.json({success: false, msg:'Failed to register user'});
} else {
res.json({success: true, msg:'User registered'});
}
});
});
module.exports = router;
What I see in my collection:
{
"_id": {
"$oid": "5937b36bafdd733088cb27d0"
},
"__v": 0
}
You should learn about what is mongoose statics and methods.
In User model you should be declaring the functions as methods and statics based on the way you want it.
const bcrypt = require ('bcryptjs');
const config = require ('../config/database');
//UserSchema
const UserSchema = mongoose.Schema({
username: {
type: String,
//required: true
},
password: {
type: String,
//required: true
}
});
//To user function outside
UserSchema.statics.getUserById = function(id, callback){
User.findById(id,callback);
}
UserSchema.statics.getUserByUsername= function(username, callback){
const query = {username: username}
User.findOne (query,callback);
}
UserSchema.methods.addUser = function (callback) {
bcrypt.genSalt(10, (err, salt) =>{
bcrypt.hash(newUser.password, salt, (err, hash) => {
this.password = hash;
this.save(callback);
});
});
}
exports.User = mongoose.model("User", UserSchema);
In your controller user file, you should use addUser with your instance of the User model not on the Model you exporting. Check below..
const express = require('express');
const router = express.Router();
const passport = require('passport');
const jwt = require('jsonwebtoken');
const User = require('../modules/user');
// Register
router.post('/register', (req, res, next) => {
let newUser = new User({
username: req.body.username,
password: req.body.password
});
newUser.addUser(function(err, user) => {
if(err){
res.json({success: false, msg:'Failed to register user'});
} else {
res.json({success: true, msg:'User registered'});
}
});
});
module.exports = router;
I'm making a User Authentication with passport. First I created a default Admin User. Now this Admin must able to create users but not any other users. For this I created a Admin user in Database. Now my Question is how to create the other users by the Admin and as well only this Admin should have access to all API's routes but not for any other Users how to protect the API's? In server.js file i created middleware function as
//Catch unauthorized errors
app.use(function (err, req, res, next) {
if(err.name === 'UnauthorizedError') {
res.status(401);
res.json({"message": err.name + ":" + err.message});
}
});
Please help with this. I hope you guys don't mind for posting such a long files.
'authentication.js'
'use strict';
var passport = require('passport'),
mongoose = require('mongoose'),
Users = mongoose.model('Users');
var authentication = {
register: function(req, res, name, email, password) {
var userData = req.body;
var user = new Users({
email: userData.email,
name: userData.name,
});
user.setPassword(userData.password);
if(!user) {
res.status(400).send({error: 'All fields required'});
}
user.save(function(err, result) {
if(err) {
console.log('Could not save the User');
res.status(500).send({error: 'Could not save the User'});
}else {
res.send('New User Created Successfully');
}
});
},
login: function (req, res) {
if(!req.body.email || !req.body.password) {
res.status(400).send({"message": "All fields required"});
return;
}
passport.authenticate('local', function (err, user, info) {
var token;
if (err) {
res.status(404).send({err: 'An Error Occured'});
return;
}
if(user) {
token = user.generateJwt();
res.status(300).send({"token": token});
}else {
res.status(401).send('Unauthorized User');
}
});
}
};
module.exports = authentication;
'user-model.js'
'use strict';
var mongoose = require('mongoose'),
crypto = require('crypto'),
jwt = require('jsonwebtoken'),
Schema = mongoose.Schema;
var userSchema = new mongoose.Schema({
email: {
type: String,
required: true,
unique: true
},
name: {
type: String,
required: true
},
hash: String,
salt: String
});
userSchema.methods.setPassword = function (password) {
this.salt = crypto.randomBytes(16).toString('hex');
this.hash = crypto.pbkdf2Sync(password, this.salt, 1000, 64).toString('hex');
};
//Validating a submitted password
userSchema.methods.validPassword = function (password) {
var hash = crypto.pbkdf2Sync(password, this.salt, 1000, 64).toString('hex');
return this.hash === hash;
};
//Generating a JSON Web Token
userSchema.methods.generateJwt = function () {
var expiry = new Date();
expiry.setDate(expiry.getDate() + 7);
return jwt.sign({
_id: this._id,
email: this.email,
name: this.name,
exp: parseInt(expiry.getTime() / 1000)
}, process.env.JWT_SECRET);
};
var User = mongoose.model('Users', userSchema);
var user = new User();
user.name = 'Arjun Kumar';
user.email = 'arjun#kumar.com';
user.setPassword('myPassword');
user.save();
'user-route.js'
'use strict';
var express = require('express'),
userRoute = express.Router(),
jwt = require('express-jwt'),
authentication = require('../controllers/authentication');
var auth = jwt({
secret: process.env.JWT_SECRET,
userProperty: 'payload'
});
userRoute.post('/:adminuserid/register', auth, authentication.register)
.post('/login', authentication.login);
module.exports = userRoute;
'passport.js'
var passport = require('passport'),
LocalStrategy = require('passport-local').Strategy,
mongoose = require('mongoose'),
Users = mongoose.model('Users');
passport.use(new LocalStrategy({usernameField: 'email'}, function (username, password, done) {
Users.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);
});
}));
One thing you can do it to put all your functions in a conditional like this to give access only to admin:
If(req.user.email === your admin email) {
Your function
}
This should go under the routes that you want only the admin have access to.
Or if you have several admins , then you should alter your schema a bit and add an admin : Number which you can later declare for example any user with admin:1 are system administrators otherwise not .
I hope I understood your question correctly.
Good luck
I am creating an application for online course.
I have created a schema for user registration. In the schema, I also want to add the name of courses a user in enrolled. Course Name being an array.
User registration is successful. after that I have created a route for /dashboard, where the user sends the POST request to add the course name. That course should be added in the same registration schema field for course Enrolled. However When I save a new object of registration schema, it creates a new document field courseEnrolled field. I want this POST request value to be added in the user's document field of courseEnrolled as an array.
Here is how I have defined my registration schema. Its name in account.js
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var passportLocalMongoose = require('passport-local-mongoose');
var courseSchema = new Schema({ courseName : String });
var Account = new Schema({
username: {
type: String,
unique: true
},
password: String,
email: String,
firstName: String,
lastName: String,
courseEnrolled: [{courseName : String}]
});
Account.plugin(passportLocalMongoose);
module.exports = mongoose.model('Account', Account);
Here is my passport registration . register.js
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
var User = require('../models/account');
var bCrypt = require('bcrypt-nodejs');
var course = require('../models/courseEnrollment');
module.exports = function(passport){
passport.use('register', new LocalStrategy({
passReqToCallback : true // allows us to pass back the entire request to the callback
},
function(req, username, password, done) {
findOrCreateUser = function(){
// find a user in Mongo with provided username
User.findOne({ 'username' : username }, function(err, user) {
// In case of any error, return using the done method
if (err){
console.log('Error in SignUp: '+err);
return done(err);
}
// already exists
if (user) {
console.log('User already exists with username: '+username);
return done(null, false, req.flash('message','User Already Exists'));
} else {
// if there is no user with that email
// create the user
var newUser = new User();
var newCourse = new course();
// set the user's local credentials
newUser.username = username;
newUser.password = createHash(password);
newUser.email = req.body.email;
newUser.firstName = req.body.firstName;
newUser.lastName = req.body.lastName;
newUser.courseEnrolled = req.body.courseEnrolled;
// save the user
newUser.save(function(err) {
if (err){
console.log('Error in Saving user: '+err);
throw err;
}
console.log('User Registration succesful');
return done(null, newUser);
});
}
});
};
// Delay the execution of findOrCreateUser and execute the method
// in the next tick of the event loop
process.nextTick(findOrCreateUser);
})
);
// Generates hash using bCrypt
var createHash = function(password){
return bCrypt.hashSync(password, bCrypt.genSaltSync(10), null);
}
}
I can register a user successfully. After that I have a route for /dashboard, where I handle the POST request to add a course.
Here is the snippet of my /dashboard handling POST request.
var User = require('../models/account');
/* POST dashboard Page */
router.post('/dashboard', isAuthenticated, function (req, res) {
sess = req.session.passport.user;
console.log('session value is: ' + sess);
var newUser = new User();
console.log('newUser id is: ' + newUser._id);
var currentUser = req.user._id;
console.log('current User id is: ' + currentUser);
var myUser = req.user;
console.log('myUsers value is: ' + myUser);
var myUserCourse = req.user.courseEnrolled;
if (sess == currentUser) {
//var newCourse = new course();
console.log('request received: ' + req.body.courseEnrolled);
req.user.courseEnrolled = req.body.courseEnrolled;
newUser.save(function (err, data) {
if(error)
throw error;
else {
console.log('course Updated');
}
});
res.render('home', {user: req.user});
}
});
This newUser.save() function creates a new document in the mongodb and store the courseEnrolled. I want to store the value of req.body.courseEnrolled in the same document field where other user value is defined.
This is getting stored in collection:- 'accounts' for the user
{
"_id" : ObjectId("57f95afd9c78b91c69334f0d"),
"lastName" : "Nehra",
"firstName" : "Ashish",
"email" : "ashish.nehra#stanford.edu",
"password" : "$2a$10$YzLvbQTHFtq5l0ooP0njOux94Rp.pm.Pkb/TugBnCSTUJNhBBonLG",
"username" : "ashish",
"courseEnrolled" : [
"about to change something now"
],
"__v" : 1
}
And there is a new document being created like this in the same collection.
{
"_id" : ObjectId("5803fc4342ca1d3167102300"),
"courseEnrolled" : [ ],
"__v" : 0
}
This is logical because first you do a it on various user objects (req.user / new user):
**var newUser = new User();
This will create a new User object, and then:
newUser.save(function (err, data) {
This will save the newly created user into a new document. If you want to use the .save, rewrite it to (reused your own code):
User.findOne({ 'username' : username }, function(err, user) {
// In case of any error, return using the done method
if (err){
console.log('Error in SignUp: '+err);
return done(err);
}
user.courseEnrolled = req.body.courseEnrolled;
user.save(function (err, data) {
if(err)
throw err;
else {
console.log('course Updated');
}
});
Need to knoe why I'mgetting this error? is my approach for validating the user thorugh login form correct here? I'm just new to node.js need your help.
var mongo = require('mongodb');
var mongoose = require('mongoose');
var db = mongoose.connect('mongodb://localhost/subscribe');
var mySchema = new mongoose.Schema({
_id : String,
name : String,
phone : String,
age : Number,
password : String
});
var User = mongoose.model('signups', mySchema);
Signup form , to save the registered user in the mongodb collection.
router.post('/signup', function(req, res) {
var user = new User({
_id : req.body.email,
phone : req.body.phone,
age : req.body.age,
password : req.body.password
});
user.save(function (err, doc) {
if (err) {
res.send("There was a problem adding the information to the database.");
}
else {
res.redirect('/');
}
});
});
trying to validate the user credentials
router.post('/adduser',function(req, res){
db.signups.findOne({ $and: [{_id: req.body.useremail}, {password: req.body.password }]}, function(err, item) {
if (err) return res.send("Please provide valid credentials.");
else {
res.redirect('/home');
}
});
});
How to validate the user credentials here?