Am new in Nodejs, Am trying to create user login system with mongoDB and passportjs. Here i want to change collection name.
i refer this video here
This is my code:
Database connection js
config/database.js
module.exports = {
database : 'mongodb://localhost:27017/my_first_project',
secret : 'Mysecret'
}
models/admin.js
const mongoose = require('mongoose');
let AdminSchema = mongoose.Schema({
name:{
type: String,
required: true
},
email:{
type: String,
required: true
},
username:{
type: String,
required: true
},
password:{
type: String,
required: true
},
created_on:{
type: Date,
default: Date.now
},
status:{
type: Number,
required: true,
validate : {
validator : Number.isInteger,
message : '{VALUE} is not an integer value'
}
}
});
const Admin = module.exports = mongoose.model('User', AdminSchema);
config/passport.js
const LocalStorage = require('passport-local').Strategy;
const User = require('../models/admin');
const config = require('../config/database');
const bcrypt = require('bcryptjs');
module.exports = function(passport){
//Local Strategy
passport.use(new LocalStorage(function(username, password, done){
//Check username
let query = {username:username};
console.log(query);
User.findOne(query, function(err, user){
console.log(user);
if(err) throw err;
if(!user)
{
return done(null, false, {message: 'No user found'});
}
//check password
bcrypt.compare(password, user.password, function(err, isMatch){
if(err) throw err;
if(isMatch){
return done(null, user);
}else{
return done(null, false, {message: 'Wrong password'});
}
});
});
}));
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user);
});
});
}
This is my database structure
When i user correct username and password for login passportjs shows null.
Need to change collection name as admin.
I don't know how to ask this question. If am asking wrong sorry for all.
You can use this structure.
API structure of mongoose.model is this:
Mongoose#model(name, [schema], [collection], [skipInit])
which means
mongoose.model('User', AdminSchema, 'admin');
//Here 3rd argument 'admin': as collection name
You can do with this too
let AdminSchema = mongoose.Schema({
username : String,
password : String.
.......
}, { collection: 'admin' });
See this link from the Mongoose documentation.
Related
Am new in Nodejs, Am trying to create user login system with mongoDB and passportjs. Here i want to change collection name.
i refer this video here
This is my code:
Database connection js
config/database.js
module.exports = {
database : 'mongodb://localhost:27017/my_first_project',
secret : 'Mysecret'
}
models/admin.js
const mongoose = require('mongoose');
let AdminSchema = mongoose.Schema({
name:{
type: String,
required: true
},
email:{
type: String,
required: true
},
username:{
type: String,
required: true
},
password:{
type: String,
required: true
},
created_on:{
type: Date,
default: Date.now
},
status:{
type: Number,
required: true,
validate : {
validator : Number.isInteger,
message : '{VALUE} is not an integer value'
}
}
});
const Admin = module.exports = mongoose.model('User', AdminSchema);
config/passport.js
const LocalStorage = require('passport-local').Strategy;
const User = require('../models/admin');
const config = require('../config/database');
const bcrypt = require('bcryptjs');
module.exports = function(passport){
//Local Strategy
passport.use(new LocalStorage(function(username, password, done){
//Check username
let query = {username:username};
console.log(query);
User.findOne(query, function(err, user){
console.log(user);
if(err) throw err;
if(!user)
{
return done(null, false, {message: 'No user found'});
}
//check password
bcrypt.compare(password, user.password, function(err, isMatch){
if(err) throw err;
if(isMatch){
return done(null, user);
}else{
return done(null, false, {message: 'Wrong password'});
}
});
});
}));
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user);
});
});
}
This is my database structure
When i user correct username and password for login passportjs shows null.
Need to change collection name as admin.
I don't know how to ask this question. If am asking wrong sorry for all.
You can use this structure.
API structure of mongoose.model is this:
Mongoose#model(name, [schema], [collection], [skipInit])
which means
mongoose.model('User', AdminSchema, 'admin');
//Here 3rd argument 'admin': as collection name
You can do with this too
let AdminSchema = mongoose.Schema({
username : String,
password : String.
.......
}, { collection: 'admin' });
See this link from the Mongoose documentation.
Edit: [how to handle case of jwt expiration ]
I have read some article on how to implement email verification for your web application and each one follow up:
Creating a unique string, saving it in db with reference to user being verified and sending that unique string as a link for verification. When user visits that link, unique string is run against db and refernced user is validated.
But, I tried it in a different way, that user model contains verify status and will be false by default and when new user sign_up then a jwt token is created and that is sent to user as verification link and when the link is visited, jwt token is verified and user verify status is changed to true.
Above implementation worked for me and removes the use of creating and storing token in separate db but I am afraid this approach might have problems which I might not be aware of. here's the code for above.
passport configuration for auth(config-passport.js)
const bcrypt = require('bcrypt')
const LocalStrategy = require('passport-local').Strategy
const { User } = require('./models/user');
module.exports = (passport) => {
// passport local strategy
const authUser = (email, password, done) => {
User.findOne({ email: email }, function(err, user){
if(err) return done(err);
if(!user || !user.verify) return done(null, false);
if(user.verify){
bcrypt.compare(password, user.password, (err, isValid) => {
if (err) {
return done(err)
}
if (!isValid) {
return done(null, false)
}
return done(null, user)
})
}
})
}
passport.serializeUser((user, done) => {
done(null, user.id)
});
passport.deserializeUser((id, done) => {
User.findOne({ _id: id }, function(err, user){
done(err, user)
});
});
passport.use(new LocalStrategy({
usernameField: 'email'
}, authUser));
}
user model
'use strict';
const mongoose = require('mongoose');
const bcrypt = require('bcrypt')
const Joi = require('joi');
const Schema = mongoose.Schema;
//any changes done to userSchema will need changes done to userValidation.js
const userSchema = new Schema({
username: {type: String, required: true, maxlength: 100},
email: {type: String, unique: true, lowercase: true, required: true},
mobile: {type: Number, unique: true, required: true},
password: {type: String, required: true},
verify: { type: Boolean, enum: [false, true], default: false },
lib: [{ type: Schema.Types.ObjectId, ref: 'Book' }],
book_id: [{ type: Schema.Types.ObjectId, ref: 'Book' }]
});
const JoiValidUser = Joi.object({
username: Joi.string().min(3).max(50).required(),
email: Joi.string().email().min(5).max(50).required(),
mobile: Joi.string().regex(/^[0-9]{10}$/).required().messages({ 'string.pattern.base': `Phone number must have 10 digits.` }),
password: Joi.string().min(5).max(255).required()
});
userSchema.pre('save', async function(next){
const user = this;
const hash = await bcrypt.hash(user.password, 10);
this.password = hash;
next();
})
userSchema.methods.isValidPassword = async function(password) {
const user = this;
const compare = await bcrypt.compare(password, user.password);
return compare;
}
const User = mongoose.model('User', userSchema);
module.exports = { User, JoiValidUser };
user creation controller(userCreate.js)
const { User, JoiValidUser } = require('../models/user');
const mailer = require('../controller/mailHandler')
//takes data posted and form it in a readable format
//then validate/sanitize it against schema
//if error arises or user already exists a msg is passed on
//else user creation process is executed
module.exports = async function(req, res){
let user = {
username: req.body.username,
email: req.body.email,
mobile: req.body.mobile,
password: req.body.password
}
try{
JoiValidUser.validate(user);
const ExistUser = await User.findOne({
$or: [
{ email: req.body.email },
{ mobile: req.body.mobile }
]
});
if(ExistUser)
throw new Error("Email/Mobile Number already Registered");
await (new User(user)).save();
mailer(user.username, user.email);
res.send({ msg: "A Verification link is sent to mail" });
} catch(err) {
res.render('error', { message: err.message })
}
}
user verification route (verify.js)
const router = require('express').Router();
const jwt = require('jsonwebtoken');
const config = require('dotenv').config().parsed
const { User } = require('../models/user')
const routePlan = require('../route_plan');
router.get('/:token', async(req, res) => {
const { email } = jwt.verify(req.params.token, config.SECRET);
await User.findOneAndUpdate({ email: email }, {
$set: { verify: true }
});
res.send("Welcome ...")
})
module.exports = router;
EDIT:
Thank you all for your feedback but there is another problem I want to be clear of on how to handle case when jwt token expires because link will be invalid and user cannot try to sign up again because his info is already in db and he cannot register again
Am new in Nodejs, Am trying to create user login system with mongoDB and passportjs. Here i want to change collection name.
i refer this video here
This is my code:
Database connection js
config/database.js
module.exports = {
database : 'mongodb://localhost:27017/my_first_project',
secret : 'Mysecret'
}
models/admin.js
const mongoose = require('mongoose');
let AdminSchema = mongoose.Schema({
name:{
type: String,
required: true
},
email:{
type: String,
required: true
},
username:{
type: String,
required: true
},
password:{
type: String,
required: true
},
created_on:{
type: Date,
default: Date.now
},
status:{
type: Number,
required: true,
validate : {
validator : Number.isInteger,
message : '{VALUE} is not an integer value'
}
}
});
const Admin = module.exports = mongoose.model('User', AdminSchema);
config/passport.js
const LocalStorage = require('passport-local').Strategy;
const User = require('../models/admin');
const config = require('../config/database');
const bcrypt = require('bcryptjs');
module.exports = function(passport){
//Local Strategy
passport.use(new LocalStorage(function(username, password, done){
//Check username
let query = {username:username};
console.log(query);
User.findOne(query, function(err, user){
console.log(user);
if(err) throw err;
if(!user)
{
return done(null, false, {message: 'No user found'});
}
//check password
bcrypt.compare(password, user.password, function(err, isMatch){
if(err) throw err;
if(isMatch){
return done(null, user);
}else{
return done(null, false, {message: 'Wrong password'});
}
});
});
}));
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user);
});
});
}
This is my database structure
When i user correct username and password for login passportjs shows null.
Need to change collection name as admin.
I don't know how to ask this question. If am asking wrong sorry for all.
You can use this structure.
API structure of mongoose.model is this:
Mongoose#model(name, [schema], [collection], [skipInit])
which means
mongoose.model('User', AdminSchema, 'admin');
//Here 3rd argument 'admin': as collection name
You can do with this too
let AdminSchema = mongoose.Schema({
username : String,
password : String.
.......
}, { collection: 'admin' });
See this link from the Mongoose documentation.
I am getting an error ValidationError: Path password is required, when trying to register a new User. I am new to Node, programming, and authentication.
Here is the code I have, and then I will walk through what I have done to try to trouble shoot it.
User model and schema
var mongoose = require("mongoose");
var passportLocalMongoose = require("passport-local-mongoose");
var bcrypt = require("bcrypt-nodejs");
var UserSchema = new mongoose.Schema({
username: {type: String, unique: true, required: true},
password: {type: String, unique: true, required: true},
// avatar: String,
firstName: String,
lastName: String,
email: {type: String, unique: true, required: true},
resetPasswordToken: String,
resetPasswordExpires: Date,
// isAdmin: {type: Boolean, default: false}
});
UserSchema.plugin(passportLocalMongoose);
//save function -- hashes and then saves the password
UserSchema.pre('save', function(next) {
var user = this;
var SALT_FACTOR = 5;
if (!user.isModified('password')) return next();
bcrypt.genSalt(SALT_FACTOR, function(err, salt) {
if (err) return next(err);
bcrypt.hash(user.password, salt, null, function(err, hash) {
if (err) return next(err);
user.password = hash;
next();
});
});
});
//compares and checks password
UserSchema.methods.comparePassword = function(candidatePassword, cb) {
bcrypt.compare(candidatePassword, this.password, function(err, isMatch) {
if (err) return cb(err);
cb(null, isMatch);
});
};
module.exports = mongoose.model("User", UserSchema);
Here is my app.js file:
var express = require("express"),
app = express(),
bodyParser = require("body-parser"),
mongoose = require("mongoose"),
expressSanitizer = require("express-sanitizer"),
passport = require("passport"),
cookieParser = require("cookie-parser"),
LocalStrategy = require("passport-local"),
session = require("express-session"),
nodemailer = require("nodemailer"),
bcrypt = require("bcrypt-nodejs"),
async = require("async"),
crypto = require("crypto"),
flash = require("connect-flash"),
moment = require("moment"),
User = require("./models/user"),
// seedDB = require("./seeds"),
methodOverride = require("method-override");
// APP CONFIG
mongoose.connect("mongodb://localhost/blog", {useMongoClient: true});
//PRODUCTION CONFIG - LIVE URL GOES HERE!
app.set("view engine", "ejs");
app.use(express.static(__dirname + "/assets"));
app.use(bodyParser.urlencoded({extended: true}));
app.use(expressSanitizer());
app.use(methodOverride("_method"));
app.use(cookieParser('secret'));
//require moment
app.locals.moment = require('moment');
// seedDB(); //seed test data!
// PASSPORT CONFIGURATION
app.use(require("express-session")({
secret: "It's a secret to everyone!!",
resave: false,
saveUninitialized: false
}));
app.use(passport.initialize());
app.use(passport.session());
app.use(flash());
passport.use(new LocalStrategy(function(username, password, done) {
User.findOne({ username: username }, function(err, user) {
if (err) return done(err);
if (!user) return done(null, false, { message: 'Incorrect username.' });
console.log(user);
User.comparePassword(password, function(err, isMatch) {
if (isMatch) {
return done(null, user);
} else {
return done(null, false, { message: 'Incorrect password.' });
}
});
});
}));
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user);
});
});
app.use(function(req, res, next){
res.locals.currentUser = req.user;
res.locals.success = req.flash('success');
res.locals.error = req.flash('error');
next();
});
// REQUIRE ROUTES
var commentRoutes = require("./routes/comments"),
bpostRoutes = require("./routes/bposts"),
indexRoutes = require("./routes/index");
//USE ROUTES
app.use("/", indexRoutes);
app.use("/bposts", bpostRoutes);
app.use("/bposts/:id/comments", commentRoutes);
//RUN SERVER
app.listen(process.env.PORT, process.env.IP, function(){
console.log("The Server Has Started!");
});
Here is the index.js route file for posting new Users
// handle sign up logic
router.post("/register", function(req, res) {
var newUser = new User({
username: req.sanitize(req.body.username),
firstName:req.sanitize(req.body.firstName),
lastName: req.sanitize(req.body.lastName),
email: req.sanitize(req.body.email),
});
console.log(newUser);
// if (req.body.adminCode === 'secretcode123') {
// newUser.isAdmin = true;
// }
User.register(newUser, req.body.password, function(err, user) {
if (err) {
console.log(err);
return res.render("register", { error: err.message });
}
passport.authenticate("local")(req, res, function() {
req.flash("success", "Successfully Signed Up! Nice to meet you " + req.body.username);
res.redirect("/bposts");
});
});
});
As you can see in the index.js
I did a console.log(newUser) and it brings back the entire newUser object.
{ username: 'apple',
firstName: 'apple',
lastName: 'apple',
email: 'apple#gmail.com',
_id: 5a2eac41720dc60acffa02f4 }
Outside of this I am not sure what other testing I could do to locate whats happening behind the scenes. Any help or pointers on how to think about this kind of problem in the future so I can resolve it myself would be awesome. Thanks.
Here is the full StackTrace as well if it helps:
{ ValidationError: User validation failed: password: Path `password` is required.
at MongooseError.ValidationError.inspect (/home/ubuntu/workspace/node_modules/mongoose/lib/error/validation.js:57:23)
at formatValue (util.js:351:36)
at inspect (util.js:185:10)
at exports.format (util.js:71:24)
at Console.log (console.js:43:37)
at /home/ubuntu/workspace/routes/index.js:35:21
at /home/ubuntu/workspace/node_modules/passport-local-mongoose/index.js:213:33
at /home/ubuntu/workspace/node_modules/mongoose/lib/model.js:4038:16
at /home/ubuntu/workspace/node_modules/mongoose/lib/services/model/applyHooks.js:175:17
at _combinedTickCallback (internal/process/next_tick.js:73:7)
at process._tickCallback (internal/process/next_tick.js:104:9)
errors:
{ password:
{ ValidatorError: Path `password` is required.
at MongooseError.ValidatorError (/home/ubuntu/workspace/node_modules/mongoose/lib/error/validator.js:25:11)
at validate (/home/ubuntu/workspace/node_modules/mongoose/lib/schematype.js:782:13)
at /home/ubuntu/workspace/node_modules/mongoose/lib/schematype.js:829:11
at Array.forEach (native)
at SchemaString.SchemaType.doValidate (/home/ubuntu/workspace/node_modules/mongoose/lib/schematype.js:789:19)
at /home/ubuntu/workspace/node_modules/mongoose/lib/document.js:1528:9
at _combinedTickCallback (internal/process/next_tick.js:73:7)
at process._tickCallback (internal/process/next_tick.js:104:9)
message: 'Path `password` is required.',
name: 'ValidatorError',
properties: [Object],
kind: 'required',
path: 'password',
value: undefined,
reason: undefined,
'$isValidatorError': true } },
_message: 'User validation failed',
name: 'ValidationError' }
Basically you defined that you need a password in your models.
var UserSchema = new mongoose.Schema({
username: {type: String, unique: true, required: true},
password: {type: String, unique: true, required: true}, // <-- See here
firstName: String,
lastName: String,
email: {type: String, unique: true, required: true},
resetPasswordToken: String,
resetPasswordExpires: Date
});
But when you are saving you aren't passing it a password:
var newUser = new User({
username: req.sanitize(req.body.username),
firstName:req.sanitize(req.body.firstName),
lastName: req.sanitize(req.body.lastName),
email: req.sanitize(req.body.email),
/* password: req.sanitize(req.body.password) // <- Probably missing something like this.*/
});
I didn't write the following blog, but I think it might be helpful for you.
https://github.com/DDCreationStudios/Writing/blob/master/articles/AuthenticationIntro.md#user-registration
The way you did the passport authentication, it does not need to have the password required true in the user schema. Passport will automatically check the password if it's missing. If password is missing, passport will throw an error. So no need to make the password required true because passport will do the same job internally. This worked for me.
user.js
var userSchema=new mongoose.Schema({
username:{type:String,required:true},
email:{type:String,required:true},
password:String
});
server.js
app.post("/signUp",(req,res)=>{
User.register(new User({username:req.body.username,email:req.body.email}),req.body.password,(err,user)=>{
if(err){
console.log(err);
}else{
passport.authenticate("local")(req,res,()=>{
res.redirect("/");
});
}
});
});
var UserSchema = new mongoose.Schema({
username: {type: String, unique: true, required: true},
password: String ,//remove previous line and just add String to password field.
// avatar: String,
firstName: String,
lastName: String,
email: {type: String, unique: true, required: true},
resetPasswordToken: String,
resetPasswordExpires: Date,
// isAdmin: {type: Boolean, default: false}
});
You had this issue because you set the password field in your schema to
password: {type: String, required: true},
. You do not need that.
Use the info below and you will be fine.
password: {type: String}
See the working code below:
**user.js**
const mongoose = require('mongoose');
const passportLocalMongoose = require('passport-local-mongoose');
const userSchema = new mongoose.Schema({
username: {type: String, required: true},
password: {type: String} // no need to add required!
});
**server.js**
app.post('/register', (req, res) => {
const { username, password } = req.body;
User.register(new User({ username }), password, (err,user) => {
if (err) {
return res.render('register', { message: err.message, messageType:
'error', user: req.user });
}else{
passport.authenticate('local')(req, res, () => {
res.redirect('/dashboard');
});
}
});
});
I start to make admin panel for modify users. I want to have ability to change users passwords by admin. For authentication I use passport. The I change user's password - the data is saving to mongo, but after that, user can't logIn to site.
Please, help.
My Passport configuration:
module.exports = function(passport) {
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('local-login', new LocalStrategy({
usernameField : 'email',
passwordField : 'password_first',
passReqToCallback : true
},
function(req, email, password, done) {
User.findOne({ 'email' : email }, function(err, user) {
if (err)
return done(err);
if (!user)
return done(null, false, function(req, res){
res.send({error: true})
});
if (!user.isAuthenticated )
return done(null, false, function(req, res){
res.send({error: true})
});
if (!user.validPassword(password))
return done(null, false, function(req, res){
res.send({error: true})
});
if(user){
return done(null, user);
}
});
}));
User model:
var userSchema = mongoose.Schema({
date : Date,
role : String,
newRole : { type: mongoose.Schema.Types.ObjectId, ref: 'newRole'},
authToken : { type: String, required : true, unique:true },
isAuthenticated : { type: Boolean, required : true },
username : String,
email : String,
password_first : String,
firstName : String,
lastName : String,
address : String,
phone : String,
resetPasswordToken : String,
resetPasswordExpires: Date });
Methods:
userSchema.methods.generateHash = function(password) {
return bcrypt.hashSync(password, bcrypt.genSaltSync(8), null);
};
userSchema.methods.validPassword = function(password) {
return bcrypt.compareSync(password, this.password_first);
};
var User = mongoose.model('User', userSchema);
module.exports = User;
And finally:
exports.changeByAdmin = function (req, res, next) {
User.findById({ _id : req.params.id }, function(err, updateUser) {
if(err) return next(err);
if (!updateUser) return next();
updateUser.email = req.body.email;
updateUser.firstName = req.body.firstName;
updateUser.lastName = req.body.lastName;
updateUser.address = req.body.address;
updateUser.phone = req.body.phone;
updateUser.password_first = updateUser.generateHash(req.body.password_first);
updateUser.save(function(err, updateUser) {
if(err) return next(err);
if(!updateUser) return next();
res.send({ success: true });
});
});
};
If I change only email of user, the user can login with new email and old password, but if I change password - user can't login. Please, advise the solution of this problem. Thank you.