I am trying to code a mean authentication app. Right now i can create user in mongodb via postman but when i try to authenticate it,if it has wrong username or password it gives proper feedback,wrong password etc, but if it matches correct username and password on database, server shutdowns, it doesnt gives any feedback on postman and it gives the following error on server terminal:
(node:11262) DeprecationWarning: Mongoose: mpromise (mongoose's
default promise library) is deprecated, plug in your own promise
library instead: http://mongoosejs.com/docs/promises.html
/home/cagdas/Desktop/basictest/node_modules/jsonwebtoken/sign.js:90
throw err;
^
Error: Expected object
at validate
(/home/cagdas/Desktop/basictest/node_modules/jsonwebtoken
/sign.js:35:11) at Object.module.exports [as sign]
(/home/cagdas/Desktop/basictest/node_modules/jsonwebtoken
/sign.js:101:7) at User.comparePassword
(/home/cagdas/Desktop/basictest/routes/users.js:40:26)
at bcrypt.compare (/home/cagdas/Desktop/basictest/models/user.js:52:6)
at
/home/cagdas/Desktop/basictest/node_modules/bcryptjs/dist
/bcrypt.js:297:21
at /home/cagdas/Desktop/basictest/node_modules
/bcryptjs/dist/bcrypt.js:1353:21
at Immediate.next (/home/cagdas/Desktop/basictest/node_modules
/bcryptjs/dist/bcrypt.js:1233:21)
at runCallback (timers.js:672:20)
at tryOnImmediate (timers.js:645:5)
at processImmediate [as _immediateCallback] (timers.js:617:5)
This is my code:
app.js:
const express = require('express') ;
const path = require('path');
const bodyParser = require('body-parser');
const cors = require('cors');
const passport = require('passport');
const mongoose = require('mongoose');
const config = require('./config/database');
// Connect to Database
mongoose.connect(config.database, { useMongoClient: true });
// On Connection
mongoose.connection.on('connected', () => {
console.log('Connected to database '+config.database);
});
// On Error
mongoose.connection.on('error', (err) =>{
console.log('Database error: '+err);
});
const app = express();
const users = require('./routes/users');
// Port Number
const port = 3000;
// Cors Middleware
app.use(cors());
// Set Static Folder
app.use(express.static(path.join(__dirname, 'public')));
// Body Parser Middleware
app.use(bodyParser.json());
// Passport Middleware
app.use(passport.initialize());
app.use(passport.session());
require('./config/passport')(passport);
app.use('/users', users);
// Index Route
app.get('/', (req, res) => {
res.send('Invalid Endpoint');
});
// Start Server
app.listen(port, () => {
console.log('Server started on port '+port);
});
users.js:
const express = require('express');
const router = express.Router();
const passport = require('passport');
const jwt = require('jsonwebtoken');
const User = require('../models/user');
const config = require('../config/database');
// Register
router.post('/register', (req, res, next) => {
let newUser = new User({
name: req.body.name,
email: req.body.email,
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'});
}
});
});
// Authenticate
router.post('/authenticate', (req, res, next) => {
const username = req.body.username;
const password = req.body.password;
User.getUserByUsername(username, (err, user) => {
if(err) throw err;
if(!user){
return res.json({success: false, msg: 'User not found'});
}
User.comparePassword(password, user.password, (err, isMatch) => {
if(err) throw err;
if(isMatch){
const token = jwt.sign(user, config.secret, {
expiresIn: 86400 // 1 day
});
res.json({
success: true,
token: 'JWT ' +token,
user: {
id: user._id,
name: user.name,
username: user.username,
email: user.email
}
});
} else {
return res.json({success: false, msg: 'Wrong Password'});
}
});
})
});
// Profile
router.get('/profile', (req, res, next) => {
res.send('PROFILE');
});
module.exports = router;
database.js:
module.exports = {
database: 'mongodb://localhost:27017/basictest',
secret: '123456789'
}
user.js:
const mongoose = require('mongoose');
const bcrypt = require('bcryptjs');
const config =require('../config/database');
// User Schema
const UserSchema = mongoose.Schema({
name: {
type: String
},
email: {
type: String,
required: true
},
username: {
type: String,
required: true
},
password: {
type: String,
required: true
}
});
const User = module.exports = mongoose.model('User', UserSchema);
module.exports.getUserById = function(id, callback){
User.findById(id, callback);
}
module.exports.getUserByUsername = function(username, callback){
const query = {username: username}
User.findOne(query, callback);
}
module.exports.addUser = function(newUser, callback){
bcrypt.genSalt(10, (err, salt) => {
bcrypt.hash(newUser.password, salt, (err, hash) => {
if(err) throw err;
newUser.password = hash;
newUser.save(callback);
});
});
}
module.exports.comparePassword = function(candidatePassword, hash,
callback){
bcrypt.compare(candidatePassword, hash, (err, isMatch) => {
if(err) throw err;
callback(null, isMatch);
});
}
passport.js
const JwtStrategy = require('passport-jwt').Strategy;
const ExtractJwt = require('passport-jwt').ExtractJwt;
const User = require('../models/user');
const config = require('../config/database');
module.exports = function(passport){
let opts = {};
opts.jwtFromRequest = ExtractJwt.fromAuthHeaderWithScheme('jwt');
opts.secretOrKey = config.secret;
passport.use(new JwtStrategy(opts, (jwt_payload, done) => {
User.getUserById(jwt_payload._id, (err, user) => {
if(err){
return done (err, false);
}
if(user){
return done(null, user);
} else {
return done(null, false);
}
});
}));
}
Solution - not user only { data: user }
const token = jwt.sign({data: user}, config.secret, {
expiresIn: 604800 // 1 week
});
Got the same error for a while and I suggest that you recreate a new object user (without the hashed password for security reason) and insert it into the sign function.
let restrictedUser = {
id: user._id,
username: user.username,
name: user.name,
email: user.email
}
const token = jwt.sign(restrictedUser, config.secret, {
expiresIn: "7d"
})
Hope this will help.
Not sure about this, But you can try this
"npm remove mongoose"
Then
"npm install mongoose#4.10.8 --save"
Hope it will help you..
Thanks...
Related
I am new to building REST API's, and I am trying to enter new user information into a collection called 'Users', however, when I submit the information I get Cannot POST /register
Below is my code
server.js
require('dotenv').config()
const express = require('express')
const app = express()
const mongoose = require('mongoose')
const expressValidator = require('express-validator')
mongoose.connect(process.env.DATABASE_URL, { useNewUrlParser: true , useUnifiedTopology: true })
const db = mongoose.connection
db.on('error', (error) => console.error(error))
db.once('open', () => console.log('Connected to Database'))
app.use(express.json())
const articlesRouter = require('./routes/articles')
app.use('/articles', articlesRouter)
const userRouter = require('./routes/users')
app.use('/register', userRouter)
app.set('view-engine', 'ejs')
app.use(express.urlencoded({ extended: false }))
app.get('/', (req, res) => {
res.render('index.ejs')
})
app.get('/login', (req, res) => {
res.render('login.ejs')
})
app.post('/login', (req, res) => {
})
app.get('/register', (req, res) => {
res.render('register.ejs')
})
app.listen(3000, () => console.log('Server Started'))
Users model
const mongoose = require('mongoose')
const bcrypt = require('bcrypt')
var userSchema = new mongoose.Schema({
firstName: String,
lastName: String,
email: String,
password: String,
permissionLevel: Number,
created: Date},
{ collection: 'Users'});
module.exports = mongoose.model('Users', userSchema);
module.exports.createUser = function(newUser, callback){
bcrypt.genSalt(10, function(err, salt) {
bcrypt.hash(newUser.password, salt, function(err, hash) {
newUser.password = hash;
newUser.save(callback);
});
});
}
module.exports.getUserByEmail = function(email, callback){
var query = {email: email};
User.findOne(query, callback);
}
module.exports.getUserById = function(id, callback){
user.findById(id, callback);
}
module.exports.comparePassword = function(candidatePassword, hash, callback) {
bcrypt.compare(candidatePassword, hash, function(err, isMatch){
if(err) throw err;
callback(null, isMatch)
});
}
users routes
const express = require('express')
const router = express.Router()
const Users = require('../models/users')
const passport = require('passport')
const LocalStrategy = require('passport-local').Strategy
//Register Page - GET
router.get('/register', (req, res) => {
res.render('register.hbs', {
pageTitle: 'register'
});
});
//Login Page - GET
router.get('/login', (req, res) => {
res.render('login.hbs', {
pageTitle: 'login'
});
});
//Register - POST
router.post('/', (req, res) => {
var firstName = req.body.firstName;
var lastName = req.body.lastName;
var email = req.body.email;
var password = req.body.password;
//validations
req.checkBody('firstName', 'Your First Name is Required').notEmpty();
req.checkBody('lastName', 'Your Last Name is Required').notEmpty();
req.checkBody('email', 'A valid email is required').isEmail();
req.checkBody('password', 'An Account Passowrd Is Required').notEmpty();
var errors = req.validationErrors();
if (errors) {
res.render('register', {
errors:errors
});
} else {
var newUser = new newUser({
firstName: firstName,
lastName: lastName,
email: email,
password: password,
});
User.createUser(newUser, function(err, user) {
if(err) throw(err);
console.log(user);
});
req.flash('success_message', "You are now registered!");
res.redirect('/login');
}
});
passport.use(new LocalStrategy({
email: 'email'
},
function(email, password, done){
Users.getUserByEmail(email, function(err, Users){
if(err) throw err;
if(!Users){
return done(null, false, {message: 'Unknown Email Address'});
}
Users.comparePassword(password, user.password, function(err, ismatch){
if(err) throw err;
if(ismatch){
return done (null, user);
} else {
return done(null, false, {message: 'Invalid Passowrd'});
}
});
});
}));
passport.serializeUser(function(user, done){
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
Users.getUserByID(id, function(err, user) {
done(err, user)
});
});
router.post('/login', passport.authenticate('local', {
successRedirect: '/dashboard',
failureRedirect: '/login',
successFlash: 'Welcome',
failureFlash: 'Invalid Email or Passowrd!'
}), function(req, res) {
res.redirect('/');
});
router.get('/logout', function(req, res) {
req.logout();
req.flash('Success_message', 'You are now logged out!')
res.redirect('/');
});
module.exports = router
I suspect the conflict is between the router /register and the my 'Users' collection. I'm just unable to pinpoint what the issue could be.
Any help would be greatly appreciated
Your userRouter is mounted on /users (from your code: app.use('/users', userRouter), meaning that all the routes you declared in the router are prefixed by /users).
Try calling POST /users/register.
You're using users routes file at /users route, so when you declare /register route in users routes file it become /users/register please send your REST API request to /users/register endpoint
I have been browsing the forum but I am unable to find the error in my code , it always results in false and I am not able to solve it , Please help me...
Is there any other way to authenticate user instead of passport so that I can use it.I am adding more sentences as it is not allowing me to post my query,Sorry..
const http = require("http"),
hostname = "127.0.0.1",
port = 3000,
bodyParser = require("body-parser"),
mongoose = require("mongoose"),
express = require("express"),
passport = require("passport"),
localStrategy = require("passport-local"),
passportLocalMongoose = require("passport-local-mongoose"),
User = require("./models/user");
app = express();
mongoose.connect("mongodb://localhost/drive", { useNewUrlParser: true });
app.set("view engine", "ejs");
app.use(express.static("public"));
app.use(passport.initialize());
app.use(passport.session());
app.use(
require("express-session")({
secret: "Beta tumse na ho payega",
resave: false,
saveUninitialized: false
})
);
passport.use(new localStrategy(User.authenticate()));
passport.serializeUser(User.serializeUser());
passport.deserializeUser(User.deserializeUser());
app.use(bodyParser.urlencoded({ extended: true }));
const server = http.createServer((req, res) => {
res.statusCode = 200;
res.setHeader("Content-Type", "text/plain");
});
app.get("/", function(req, res) {
res.render("index");
});
app.get("/register", function(req, res) {
res.send("hello");
});
app.get("/login", function(req, res) {
res.render("login");
});
app.post(
"/login",
passport.authenticate("local", {
successRedirect: "/",
failureRedirect: "/login"
}),
function(req, res) {}
);
app.get("/logout", function(req, res) {
req.logout();
res.redirect("/");
});
function isLoggedIn(req, res, next) {
if (req.isAuthenticated()) {
return next();
} else {
console.log("Not logged");
res.redirect("/login");
}
}
app.get("/secret", isLoggedIn, function(req, res) {
res.send("You are logged in");
});
app.post("/register", function(req, res) {
if (req.body.password === req.body.cpassword) {
User.register(
new User({ username: req.body.username }),
req.body.password,
function(err, user) {
if (err) console.log(err);
else
passport.authenticate("local")(req, res, function() {
res.send("signed up");
});
}
);
} else res.send("Password Mismatch");
});
//DRIVE SCHEMA
//var driveSchema = mongoose.Schema({
// title: String,
// created: { type: Date, default: Date.now }
//});
app.listen(port, hostname, function() {
console.log("Server is running at " + hostname + "/" + port);
});
//./models/user.js file
const mongoose = require("mongoose"),
passportLocalMongoose = require("passport-local-mongoose");
var UserSchema = new mongoose.Schema({
username: String,
password: String
});
UserSchema.plugin(passportLocalMongoose);
module.exports = mongoose.model("User", UserSchema);
I have used passport and jwt library, to authenticate and maintain session for the user. There is no need to maintain user session on server side.
apis/apis.js : This file has all apis end points. /login url will authenticate user using passport and send a token to the client using jwt
const passport = require('passport')
const expRoute = require('express').Router();
let exporter = process.exporter;
expRoute.post('/login', (req, res, next) => {
passport.authenticate(
'local',
{
// successRedirect: '/',
// failureRedirect: '/login',
successFlash: 'Welcome!',
failureFlash: 'Invalid username or password.'
},
(err, user, info) => {
if (err) {
return res.status(500).json(err)
}
else if (user) {
return res.status(200).json({
token: exporter.generateToken(user)
})
}
else {
return res.status(400).json(info)
}
}
)(req, res, next);
})
expRoute.get('/view', exporter.authenticateToken, (req, res) => {
let param = req.finalTokenExtractedData
if (param && exporter.isObjectValid(param, 'tokenId', true, true)) {
let condition = {
_id: param.tokenId
}
let options = {
_id: 0,
password: 0,
__v: 0
}
process.USER.findOne(condition, options) // mongo find
.then((data) => {
res.status(200).json({
result: data,
msg: 'success'
})
})
.catch((mongoErr) => {
exporter.logNow(`USER mongo Error: ${mongoErr}`)
res.status(400).json({
msg: 'user not found'
})
})
}
else {
res.status(404).json({
msg: 'invalid token'
})
}
})
module.exports = expRoute
common/env.js: This file will initialise all connections such mongo, require few files will be used as global
process.CONFIG = require('../configs/config.json')
process.exporter = require("../lib/exporter.js")
process.dbInit = (globalName, mongoUrl, collectionName) => {
require("../models/db-init.js")(mongoUrl, collectionName)
.then((modelObj) => {
process[globalName] = modelObj // will be used as global
})
.catch((dbInitErr) => {
process.exporter.logNow(`dbInit Error: ${dbInitErr}`)
process.exit()
});
}
lib/exporter.js: This file has exporter class which consist of all major functions like mongo connection, authenticateToken, verifyPassword, etc.
const fs = require('fs'),
redis = require("redis"),
path = require("path"),
mongoose = require('mongoose'); mongoose.set('useCreateIndex', true);
const Schema = mongoose.Schema;
var bcrypt = require('bcryptjs')
var jwt = require('jsonwebtoken')
class Exporter {
mongoConnection(mongoURI, schemaObj) {
return new Promise(async (resolve, reject) => {
if (!mongoURI || typeof mongoURI == 'undefined' || mongoURI.length < 1)
return reject('invalid mongo connection url');
return resolve(mongoose.createConnection(mongoURI, { useNewUrlParser: true }))
})
}
createMongoSchema(schemaObj) {
return (new Schema(schemaObj));
}
createMongoModel(mongoDB, collectionName, newSchema) {
if (newSchema)
return mongoDB.model(collectionName, newSchema)
return mongoDB.model(collectionName)
}
authenticateToken(req, res, next) {
const bearerHeader = req.header('authorization')
if (typeof bearerHeader != 'undefined') {
const bearer = bearerHeader.split(' ')
const bearerToken = bearer[1]
jwt.verify(bearerToken, process.CONFIG.jwt.token.activated, (err, data) => {
if (err)
res.status(400).json({
msg: "Invalid token or please try to login again"
})
else {
process.exporter.getSingleHashKeysValuesFromRedis('expired_token', bearerToken)
.then((redisTokendata) => {
if (redisTokendata)
res.status(400).json({
msg: "token expired"
})
else {
req.finalTokenExtractedData = data
// if (req.originalUrl.trim() == process.logoutURL.trim())
req.jwtToken = {
token: bearerToken,
secret: process.CONFIG.jwt.token.activated
}
next()
}
})
.catch((redisTokenError) => {
process.exporter.logNow(`redis token error: ${redisTokenError}`)
res.status(400).json({
msg: "Some went wrong while checking token. Please try later."
})
})
}
})
}
else
res.status(400).json({
msg: "invalid token"
})
}
generateToken(data) {
let expiry = new Date();
// expiry.setDate(expiry.getDate() + 7)
expiry.setMinutes(expiry.getMinutes() + 5)
return jwt.sign({
tokenId: data._id,
exp: parseInt(expiry.getTime() / 1000),
}, process.CONFIG.jwt.token.activated)
}
createPassword(password) {
return new Promise((resolve, reject) => {
if (typeof password == 'undefined' && password == '')
return reject('password empty')
bcrypt.hash(password, 10, async (bErr, hash) => {
if (bErr)
reject(bErr)
else
resolve(hash)
})
})
}
verifyPassword(enteredPassword, savePassword) {
return bcrypt.compareSync(enteredPassword, savePassword)
}
}
module.exports = (new Exporter());
index.js: This is file which you will execute node index.js.
const express = require('express');
const path = require('path');
const bodyParser = require('body-parser');
const passport = require('passport');
require('./common/env')
require('./configs/passport')
const app = express()
const cors = require('cors')
app.use(cors());
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({ extended: true}))
app.use(passport.initialize())
app.use(function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
next();
});
let apis = require('./apis/api')
app.use('/user', apis)
/**
* 404 Handler
*/
app.use((req, res, next)=>{
return res.status(404).send("Endpoint "+req.url +" not found");
})
/**
* if any error or exception occurred then write into a JS file so that app can be restarted
*/
process.on('uncaughtException', (err) => {
console.error(err.stack);
});
app.listen(3000, function(server) {
console.log("App listening at 3000");
});
When index.js file has been executed successfully and listening to a port 3000, then use this URL http://localhost:3000/user/login for authentication, it will accept your username and password, then authenticate using passport and send a token to the client as response. The token can contain user data in encrypted form and have expiry time.
Reference link: https://github.com/arjun-707/login-logout-jwt-nodejs
You need the express-session module.
server.js
// Require a possible config.js file with your configuration variables
const Config = require('./models/config.js');
// If the config module is formed as a class
const config = new Config();
const express = new('express');
const app = express();
const session = require('express-session');
const MongoStore = require('connect-mongo')(session);
const uuid = require('uuid/v4');
const mongoose = require('mongoose');
// Require your custom passport local strategy file
const passport = require('./models/sessions');
mongoose.connect('mongodb://localhost:27017/your_db_name', {
useNewUrlParser: true
});
// Set the session options
app.use(session({
// Use UUIDs for session IDs
genid: (req) => {
return uuid()
},
// If you want to store the session in MongoDB using mongoose
// Require your personal mon
store: new MongoStore({
mongooseConnection: mongoose.connection
}),
// Define the session secret in env variable or in config file
secret: process.env.SESSION_SECRET || config.sessionSecretKey,
resave: false,
saveUninitialized: true
}));
// Initialize the passport module
app.use(passport.initialize());
// Tell to passport to use session
app.use(passport.session());
./models/session.js
const passport = require('passport');
const LocalStrategy = require('passport-local').Strategy;
const bcrypt = require('bcrypt-nodejs');
// Your custom MongoDB connection module
const db = require('./db');
// Configure passport.js to use the local strategy
passport.use(new LocalStrategy({
usernameField: 'username'
},
(username, password, done) => {
db.User.find({
username: username // But it could use email as well
}).then(res => {
const user = JSON.parse(JSON.stringify(res[0]));
if (!user) {
return done(null, false, {
message: 'Invalid credentials.\n'
});
}
if (!bcrypt.compareSync(password, user.password)) {
return done(null, false, {
message: 'Invalid credentials.\n'
});
}
return done(null, user);
}).catch(error => done(error));
}
));
// Tell passport how to serialize the user
passport.serializeUser((user, done) => {
done(null, user._id);
});
// Tell passport how to deserialize the user
passport.deserializeUser((id, done) => {
db.User.find({
_id: id
}).then(res => {
const response = typeof res !== undefined && res.length != 0 ? JSON.parse(JSON.stringify(res[0])) : null;
done(null, response)
}).catch(error => done(error, false))
});
// Export passport for external usage
module.exports = passport;
./models/db.js
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const uuid = require('uuid/v4');
// Connect to MongoDB
mongoose.connect('mongodb://localhost:27017/your_db_name', {
useNewUrlParser: true
});
// Define the models container
let models = {};
// Prepare your user schema
const userSchema = Schema({
username: {
type: String,
required: true
},
password: {
type: String,
required: true
}
});
// Assign the user schema
models.User = mongoose.model('User', userSchema);
// Export for external usage
module.exports = models;
/*
In that way you can wrap in "models" all schemas:
const newSchema = Schema({
name: String
});
models.Newschema = mongoose.model('Newschema', newSchema);
module.exports = models;
And use it externally like:
const db = require('./models/db');
db.User.find({}).then(docs => {}).catch(err => {});
db.Newschema.find({}).then(docs => {}).catch(err => {});
Etc....
*/
./models/config.js
class Config {
constructor() {
this.sessionSecretKey = "my-awesome-secretkey";
/* And other configurations */
}
}
module.exports = Config;
/*
Externally use like:
const Config = require('./models/config');
const config = new Config();
let sessionSecretKey = config.sessionSecretKey;
*/
Then you can use the req.isAuthenticated() after the login:
// POST the username and password to '/login' router
app.post('/login', (req, res, next) => {
passport.authenticate('local', (err, user, info) => {
if (info) {
return res.send(info.message)
}
if (err) {
return next(err);
}
if (!user) {
return res.sendStatus(404); // Is a shortcut
// OR -> res.status(404).end();
// OR -> res.status(404).send('Not found'); as you like
}
req.login(user, (err) => {
if (err) return next(err);
// Store the user object retrieved from MongoDB in `req.session`
req.session.user = user;
return res.sendStatus(200); // Is a shortcut
// OR -> res.status(200).end();
// OR -> res.status(200).send('OK'); as you like
})
})(req, res, next);
});
// The logout logic
app.get('/logout', verifySession, function (req, res) {
req.session.destroy(function (err) {
req.logout();
res.redirect('/');
});
});
// Verify the session to protect private routers
function verifySession(req, res, next) {
if (req.isAuthenticated()) {
next();
} else {
// Forbidden
res.redirect('/');
// OR -> res.sendStatus(403);
// OR -> res.status(403).end();
// OR -> res.status(403).send('Forbidden'); as you like
}
}
Of course you have to run npm install with all the dependencies required defined in package.json file or manually instal with npm i express-session#latest --s for all the dependencies required: npm i module-name#latest --s.
Don't forget the
const server = app.listen(config.port || 3000, () => {
console.log(`Server running on ${server.address().port} port.`);
});
At the end of server.js file. I hope that it will be useful for you.
I am using nodeJS with the expressJS server framework. This issue is regarding passportJS not saving more than 1 user to MongoDB.
Firstly, I set up email registration with passport-local and got that to work fine. Secondly, I set up passport-facebook which essentially broke my email registration somehow. Each registration (email & facebook) now only log in just 1 user, I have to keep deleting the user from mongodb to register again. When there is 1 user in the database and I try to register another - I receive the error below.
process.nextTick(function() { throw err; });
^
TypeError: Cannot read property '1' of null at model.mongodbErrorHandler (/Users/tlr/Dropbox/Websites/app-one/node_modules/mongoose-mongodb-errors/lib/plugin.js:19:49)
at next (/Users/tlr/Dropbox/Websites/app-one/node_modules/mongoose/node_modules/kareem/index.js:145:14)
at Kareem.execPost (/Users/tlr/Dropbox/Websites/app-one/node_modules/mongoose/node_modules/kareem/index.js:193:3)
at /Users/tlr/Dropbox/Websites/app-one/node_modules/mongoose/lib/model.js:219:35
at /Users/tlr/Dropbox/Websites/app-one/node_modules/mongoose/lib/model.js:152:9
at /Users/tlr/Dropbox/Websites/app-one/node_modules/mongoose/node_modules/mongodb/lib/collection.js:524:20
at /Users/tlr/Dropbox/Websites/app-one/node_modules/mongoose/node_modules/mongodb/lib/collection.js:658:14
at handleCallback (/Users/tlr/Dropbox/Websites/app-one/node_modules/mongoose/node_modules/mongodb/lib/utils.js:95:56)
at /Users/tlr/Dropbox/Websites/app-one/node_modules/mongoose/node_modules/mongodb/lib/bulk/unordered.js:465:9
at handleCallback (/Users/tlr/Dropbox/Websites/app-one/node_modules/mongoose/node_modules/mongodb/lib/utils.js:95:56)
at resultHandler (/Users/tlr/Dropbox/Websites/app-one/node_modules/mongoose/node_modules/mongodb/lib/bulk/unordered.js:413:5)
at /Users/tlr/Dropbox/Websites/app-one/node_modules/mongoose/node_modules/mongodb-core/lib/connection/pool.js:455:18
at _combinedTickCallback (internal/process/next_tick.js:131:7)
at process._tickCallback (internal/process/next_tick.js:180:9)
I think the issue may lie around the User Schema because each Strategy saves different user information. Thus, local & facebook may not be marrying up properly in the database which causes an error? Correct me if I'm wrong.
The main files to look at are my userSchema (User.js), my routes file (index.js) where I include my FB Strategy and module imports, and my controller files (userController.js, authController.js) where I export my modules and functions.
User.js
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
mongoose.Promise = global.Promise;
const md5 = require('md5');
const validator = require('validator');
const mongodbErrorHandler = require('mongoose-mongodb-errors');
const passportLocalMongoose = require('passport-local-mongoose');
const userSchema = new Schema({
name: {
type: String,
required: 'Please supply a name',
trim: true,
},
email: {
type: String,
unique: true,
lowercase: true,
trim: true,
required: 'Please supply an email address',
validate: [validator.isEmail, 'Invalid Email Address']
},
id: String,
token: String
});
userSchema.plugin(passportLocalMongoose, { usernameField: 'email' });
userSchema.plugin(mongodbErrorHandler);
module.exports = mongoose.model('User', userSchema);
index.js
const express = require('express');
const mongoose = require('mongoose');
const passport = require('passport');
const User = mongoose.model('User');
const authIDs = require('../../oauth');
const authController = require('../controllers/authController');
const userController = require('../controllers/userController');
const FacebookStrategy = require('passport-facebook').Strategy;
const router = express.Router();
passport.use(new FacebookStrategy({
clientID: authIDs.facebook.clientID,
clientSecret: authIDs.facebook.clientSecret,
callbackURL: authIDs.facebook.callbackURL,
profileFields: ['id', 'emails', 'name'],
passReqToCallback : true
},
function(req, accessToken, refreshToken, profile, done) {
const newUser = new User();
newUser.id = profile.id;
newUser.token = accessToken;
newUser.name = profile.name.givenName;
newUser.email = profile.emails[0].value;
process.nextTick( function() {
User.findOne({id: profile.id}, (err, user) => {
if (err) {
return done(err);
}
if (!user) {
newUser.save().then( user => {
done( null, user );
} ).catch( err => {
done( err );
} );
} else {
return done(null, user);
}
});
});
}
));
router.get( '/login', userController.loginForm);
router.post( '/login', authController.login);
router.get( '/register', userController.registerForm);
router.post( '/register',
userController.validateRegister,
userController.register,
authController.login
);
router.get('/auth/facebook', authController.fbauth);
router.get('/auth/facebook/callback', authController.fbcallback);
router.get( '/logout', authController.logout);
router.get('/', (req, res) => {
res.render('home');
});
module.exports = router;
userController.js
const mongoose = require('mongoose');
const User = mongoose.model('User');
const promisify = require('es6-promisify');
exports.loginForm = (req, res) => {
res.render('login')
};
exports.registerForm = (req, res) => {
res.render('register')
};
exports.validateRegister = (req, res, next) => {
// express validator methods below
req.sanitizeBody('name');
req.checkBody('name', 'You must supply a name!').notEmpty();
req.checkBody('email', 'That email is not valid!').isEmail();
req.sanitizeBody('email').normalizeEmail({
remove_dots: false,
remove_extension: false,
gmail_remove_subaddress: false
});
req.checkBody('password', 'Password cannot be blank!').notEmpty();
req.checkBody('password-confirm', 'Confirmed password cannot be blank!').notEmpty();
req.checkBody('password-confirm', 'Oops! Your passwords do not match').equals(req.body.password);
const errors = req.validationErrors();
if (errors) {
req.flash('error', errors.map(err => err.msg));
res.render('register', { body: req.body, flashes: req.flash() });
return;
}
next();
};
exports.register = async (req, res, next) => {
const user = new User({ name: req.body.name, email: req.body.email});
const register = promisify(User.register, User);
await register(user, req.body.password);
next();
};
authController.js
const passport = require('passport');
const mongoose = require('mongoose');
const User = mongoose.model('User');
exports.login = passport.authenticate('local', {
failureRedirect: '/login',
failureFlash: 'Failed Login!',
successRedirect: '/',
successFlash: 'You are now logged in!'
});
exports.fbauth = passport.authenticate('facebook', {scope: ['email']});
exports.fbcallback = passport.authenticate('facebook', {
failureRedirect: '/login',
failureFlash: 'Failed Login!',
successRedirect: '/',
successFlash: 'You are now logged in!'
});
exports.logout = (req, res, next) => {
req.logout();
req.flash('success', 'You successfully logged out!');
res.redirect('/login')
};
Any suggestions on a fix would be suuuuuuuuper duuuuuuper highly appreciated.
OH MY GOD after hours of trying, like most problems solved in coding, this one was terribly embarrassing.
Because I'm using the code as mentioned in the question above, you can see I'm implementing both a local email registration as well as a passport-facebook authentication. So... What was causing that confusing error was the following code was like this:
passport.serializeUser(User.serializeUser(), function(user, done) {
done(null, user.id);
});
passport.deserializeUser(User.deserializeUser(), function(id, done) {
User.findById(id, function(err, user) {
done(err, user);
});
});
instead of simply being like this:
passport.serializeUser(User.serializeUser(), function(user, done) {
done(null, user);
});
passport.deserializeUser(User.deserializeUser(), function(user, done) {
done(null, user);
});
I am working on a rest api based on this tutorial from Joshmorony, I keep running on error "Could not get any response" while testing on Postman. I have tied consuming the end points on ionic3 project but still running on the same problem. What am I doing wrong that might be leading to this error? I would appreciate your support.
Here is my code.
In the controller folder controllers/authentication.js
const jwt = require('jsonwebtoken');
const bluebird = require('bluebird');
const nodemailer = require('nodemailer');
const User = require('../models/user');
const authConfig = require('../config/auth');
const crypto = bluebird.promisifyAll(require('crypto'));
/**
* Generating JWT tokens
*
*/
function generateToken(user){
return jwt.sign(user, authConfig.secret, {
expiresIn: 10080
});
}
function setUserInfo(request){
return {
_id: request._id,
email: request.email,
role: request.role
};
}
/**
* Local login authentication
*
*/
exports.login = function(req, res, next){
var userInfo = setUserInfo(req.user);
res.status(200).json({
token: 'JWT ' + generateToken(userInfo),
user: userInfo
});
}
/**
* Local registration
*
*/
exports.register = function(req, res, next){
var email = req.body.email;
var password = req.body.password;
var role = req.body.role;
if(!email){
return res.status(422).send({error: 'You must enter an email address'});
}
if(!password){
return res.status(422).send({error: 'You must enter a password'});
}
User.findOne({email: email}, function(err, existingUser){
if(err){
return next(err);
}
if(existingUser){
return res.status(422).send({error: 'That email address is already in use'});
}
var user = new User({
email: email,
password: password,
role: role
});
user.save(function(err, user){
if(err){
return next(err);
}
var userInfo = setUserInfo(user);
res.status(201).json({
token: 'JWT ' + generateToken(userInfo),
user: userInfo
})
});
});
}
/**
* Roles Creation
*
*/
exports.roleAuthorization = function(roles){
return function(req, res, next){
var user = req.user;
User.findById(user._id, function(err, foundUser){
if(err){
res.status(422).json({error: 'No user found.'});
return next(err);
}
if(roles.indexOf(foundUser.role) > -1){
return next();
}
res.status(401).json({error: 'You are not authorized to view this content'});
return next('Unauthorized');
});
}
}
In the model folder model/User.js
const mongoose = require('mongoose');
const bcrypt = require('bcrypt-nodejs');
var UserSchema = new mongoose.Schema({
email: {
type: String,
lowercase: true,
unique: true,
required: true
},
password: {
type: String,
required: true
},
role: {
type: String,
enum: ['reader', 'creator', 'editor'],
default: 'reader'
},
passwordResetToken: String,
passwordResetExpires: Date,
profile: {
name: String,
gender: String,
location: String,
picture: String
}
}, {
timestamps: true
});
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();
});
});
});
UserSchema.methods.comparePassword = function(passwordAttempt, cb){
bcrypt.compare(passwordAttempt, this.password, function(err, isMatch){
if(err){
return cb(err);
} else {
cb(null, isMatch);
}
});
}
module.exports = mongoose.model('User', UserSchema);
Routes routes.js
const AuthenticationController = require('./controllers/authentication'),
TodoController = require('./controllers/todo'),
express = require('express'),
passportService = require('./config/passport'),
passport = require('passport');
const requireAuth = passport.authenticate('jwt', {session: false}),
requireLogin = passport.authenticate('local', {session: false});
module.exports = function(app){
var apiRoutes = express.Router(),
authRoutes = express.Router(),
todoRoutes = express.Router();
// Auth Routes
apiRoutes.use('/auth', authRoutes);
authRoutes.post('/register', AuthenticationController.register);
authRoutes.post('/login', requireLogin, AuthenticationController.login);
authRoutes.post('/forgot', AuthenticationController.postForgot);
authRoutes.post('/reset/:token', AuthenticationController.postReset);
authRoutes.post('/account/profile', requireAuth, AuthenticationController.postUpdateProfile);
authRoutes.post('/account/password', requireAuth, AuthenticationController.postUpdatePassword);
authRoutes.get('/protected', requireAuth, function(req, res){
res.send({ content: 'Success'});
});
// Set up routes
app.use('/api', apiRoutes);
}
and lastly app.js file
const express = require('express');
const app = express();
const mongoose = require('mongoose');
const errorHandler = require('errorhandler');
const chalk = require('chalk');
const logger = require('morgan');
const bodyParser = require('body-parser');
const cors = require('cors');
const dotenv = require('dotenv');
const databaseConfig = require('./app/config/database');
const router = require('./app/routes');
/**
* Load environment variables from .env file, where API keys and passwords are configured.
*/
dotenv.load({ path: '.env' });
/**
* Connect to MongoDB.
*/
mongoose.Promise = global.Promise;
mongoose.createConnection(process.env.MONGODB_URI || process.env.MONGOLAB_URI);
mongoose.connection.on('error', (err) => {
console.error(err);
console.log('%s MongoDB connection error. Please make sure MongoDB is running.', chalk.red('✗'));
process.exit();
});
/**
* Express configuration.
*/
app.set('host', process.env.OPENSHIFT_NODEJS_IP || '0.0.0.0');
app.set('port', process.env.PORT || process.env.OPENSHIFT_NODEJS_PORT || 8080);
/**
* Error Handler.
*/
app.use(errorHandler());
/**
* Start Express server.
*/
app.listen(app.get('port'), () => {
console.log('%s App is running at http://localhost:%d in %s mode', chalk.green('✓'), app.get('port'), app.get('env'));
console.log(' Press CTRL-C to stop\n');
});
app.use(bodyParser.urlencoded({ extended: false })); // Parses urlencoded bodies
app.use(bodyParser.json()); // Send JSON responses
app.use(logger('dev')); // Log requests to API using morgan
app.use(cors());
router(app);
I managed to get the error, when I was ruling the app in the terminal I got this error "DeprecationWarning: open() is deprecated in mongoose >= 4.11.0, use openUri() instead, or set the useMongoClient option if using connect() or createConnection(). See http://mongoosejs.com/docs/connections.html#use-mongo-client", when I try to modify mongoose.connect(process.env.MONGODB_URI || process.env.MONGOLAB_URI); to mongoose.createConnection(process.env.MONGODB_URI || process.env.MONGOLAB_URI); thats when I get that error of "Could not get any response". Just opted to go with the old connect(). Hope this will help someone experiencing the same issue.
I am trying to save a user to mongodb database using post request as follow, but I got the error TypeError: User is not a constructor. It's a pretty simple set up of the code but i can't figure out anything wrong with it.
//models/users.js
const mongoose = require('mongoose');
const bcrypt = require('bcrypt');
const confic = require('../models/users');
// User schema
const UserSchema = mongoose.Schema({
name: {
type: String,
},
email: {
type: String,
required: true
},
username:{
type: String,
required: true
},
password: {
type: String,
required: true
}
});
const User = module.exports = mongoose.model('User', UserSchema);
module.exports.getUserById = function(id,callback){
User.findById(id,callback);
}
module.exports.getUserByUsername = function(username,callback){
const query = {username:username}
User.findOne(query,callback);
}
module.exports.addUser= function (newUser, callback) {
bcrypt.gensalt(10,(err,salt) => {
bcrypt.hash(newUser.password, salt , (err, hash) =>{
if(err) throw err;
newUser.password=hash;
newUser.save(callback);
});
});
}
routes/users.js
//routes/users.js
const express = require('express');
const router = express.Router();
const passport = require('passport');
const jwt = require('jsonwebtoken');
User = require('../models/users');
// // Register
router.post('/register', (req, res, next) => {
var newUser = new User({
name: req.body.name,
email: req.body.email,
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'});
}
});
});
// Authenticate
router.post('/authenticate', (req, res, next) => {
res.send('AUTHENTICATE');
});
// Profile
router.get('/profile', (req, res, next) => {
res.send('PROFILE');
});
module.exports = router;
I am using Postman chrome to send data but it is not showing user registered according to program.
You are exporting your user model from a different file and trying to import from a different file.
change this line:
const User = require('../config/database');
to this:
const User = require('../models/users') # import your user model