I am getting Error: next is not defined. I don't know in which file I am getting this error.
Code:
server.js
require("dotenv").config({
path: "C:/Users/dogra/Documents/Web Development/Portfolio/FullStack/AdvanceAuth/.env",
});
const express = require("express");
const connectDB = require("./config/db");
const errorHandler = require("./middleware/errorHandler");
connectDB();
const app = express();
app.use(express.json());
app.use("/api/auth", require("./routes/authRoutes"));
app.use("/api/private", require("./routes/privateRoutes"));
app.use(errorHandler);
const PORT = process.env.PORT || 5000;
const server = app.listen(PORT, () => {
console.log(`Server running on PORT: ${PORT}`);
});
process.on("unhandledRejection", (err, promise) => {
console.log(`Logged Error: ${err.message}`);
server.close(() => process.exit(1));
});
controllers
authController.js
const User = require("../models/userModel");
const ErrorResponse = require("../utils/errorResponse");
exports.register = async (req, res, next) => {
const { username, email, password } = req.body;
try {
const user = await User.create({
username,
email,
password,
});
sendToken(user, 201, res);
} catch (error) {
next(error);
}
};
exports.login = async (req, res, next) => {
const { email, password } = req.body;
if (!email || !password) {
return next(new ErrorResponse("Please enter credentials properly", 400));
}
try {
const user = await User.findOne({ email }).select("+password");
if (!user) {
return next(new ErrorResponse("Email not registered", 401));
}
const isMatch = await user.matchPasswords(password);
if (!isMatch) {
return next(new ErrorResponse("Invalid Password", 401));
}
sendToken(user, 200, res);
} catch (error) {
next();
}
};
exports.forgotPassword = (req, res, next) => {
res.send("ForgotPassword Route");
};
exports.resetPassword = (req, res, next) => {
res.send("ResetPassword Route");
};
const sendToken = (user, statusCode, res) => {
const token = user.getSignedJwtToken();
res.status(statusCode).json({ success: true, token });
};
privateController.js
exports.private = (req, res, next) => {
res.status(200).json({
success: true,
data: "You got access to the private data in this route",
});
};
routes
authRoutes.js
const express = require("express");
const router = express.Router();
// controllers
const {
register,
login,
forgotPassword,
resetPassword,
} = require("../controllers/authControllers");
router.route("/register").post(register);
router.route("/login").post(login);
router.route("/forgotPassword").post(forgotPassword);
router.route("/resetPassword/:resetToken").put(resetPassword);
module.exports = router;
privateRoutes.js
const express = require("express");
const router = express.Router();
const { private } = require("../controllers/privateControllers");
const { protect } = require("../middleware/authMiddleware");
router.route("/").get(protect, private);
module.exports = router;
middlewre
authMiddleware.js
const jwt = require("jsonwebtoken");
const User = require("../models/userModel");
const ErrorResponse = require("../utils/errorResponse");
exports.protect = async (req, res, next) => {
let token;
if (
req.headers.authorization &&
req.headers.authorization.startsWith("Bearer")
) {
token = req.headers.authorization.split(" ")[1];
}
if (!token) {
return next(
new ErrorResponse("Not authorized to access to this route", 401)
);
}
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
const user = await User.findById(decoded.id);
if (!user) {
return next(new ErrorResponse("No User found with this id", 404));
}
req.user = user;
next();
} catch (error) {
return next(new ErrorResponse("Not Authorized to access this route", 401));
}
};
errorHandler.js
const ErrorResponse = require("../utils/errorResponse");
const errorHandler = (err, req, res, next) => {
let error = { ...err };
error.message = err.message;
if (err.code === 11000) {
const message = `Duplicate Field value entered`;
error = new ErrorResponse(message, 400);
}
if (err.name === "ValidationError") {
const message = Object.values(err.errors).map((val) => val.message);
error = new ErrorResponse(message, 400);
}
console.log(error.message);
res.status(error.statusCode || 500).json({
success: false,
error: error.message || "Server Error",
});
};
module.exports = errorHandler;
utils
errorResponse.js
class ErrorResponse extends Error {
constructor(message, statusCode) {
super(message);
this.statusCode = statusCode;
}
}
module.exports = ErrorResponse;
models
userModel.js
const mongoose = require("mongoose");
const bcrypt = require("bcryptjs");
const jwt = require("jsonwebtoken");
const UserSchema = new mongoose.Schema({
username: {
type: String,
required: [true, "Please enter your username"],
},
email: {
type: String,
required: [true, "Please enter your email"],
unique: true,
lowercase: true,
match: [
/^(([^<>()[\]\\.,;:\s#\"]+(\.[^<>()[\]\\.,;:\s#\"]+)*)|(\".+\"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/,
,
"Please provide a valid email",
],
},
password: {
type: String,
required: [true, "Please enter a valid password"],
minlength: 8,
select: false,
},
resetPasswordToken: String,
resetPasswordExpired: Date,
});
// Hashing Password
UserSchema.pre("save", async function (next) {
if (!this.isModified("password")) {
next();
}
const salt = await bcrypt.genSalt(10);
this.password = await bcrypt.hash(this.password, salt);
next();
});
// Checking if password entered is correct or not
UserSchema.methods.matchPasswords = async function (password) {
return await bcrypt.compare(password, this.password);
};
// Converting user data into JSON WEB TOKEN
UserSchema.methods.getSignedJwtToken = function () {
return jwt.sign({ id: this._id }, process.env.JWT_SECRET, {
expiresIn: process.env.JWT_EXPIRE,
});
};
const User = mongoose.model("User", UserSchema);
module.exports = User;
Sorry, for the long question but I really don't know where is the problem. I googled, & found we get next is not defined error when you don't define it source. But, I have defined it inside my all controllers & middleware's.
The problem was in my dotenv path in server.js Since I cloned my project from GitHub in my new laptop the path was different & I forgot to update the path.
Related
I'm setting up a login route and I decided to test it with postman and it worked but later when I was checking my DB configuration I found an error when I fixed the error the login test on postman doesn't work
so in my server.js file I have :
const express = require("express");
const connectDB = require("./config/db");
const app = express();
// Connect to MongoDB
connectDB();
// Initialize middleware
app.use(express.json());
// Define routes
app.use("/api/users", require("./routes/users"));
app.use("/api/auth", require("./routes/auth"));
app.use("/api/posts", require("./routes/posts"));
app.use("/api/profile", require('./routes/profile'));
// Create server
const PORT = process.env.PORT || 5000;
app.listen(PORT, () => console.log(`Server started on port ${PORT}`));
The connectDB function in config/db.js :
const mongoose = require("mongoose");
require('dotenv').config()
const connectDB = async () => {
try {
await mongoose.connect(process.env.MONGO_URI, {
useNewUrlParser: true,
useUnifiedTopology: true,
useFindAndModify: false,
useCreateIndex: true
});
console.log("Successfully connected to MongoDB");
} catch (err) {
console.error(err.message);
}
};
module.exports = connectDB;
In the auth.js route :
const express = require("express");
const router = express.Router();
const { check } = require("express-validator");
const {auth} = require('../middlewares/auth');
const {login} = require('../controllers/authController');
// #route: POST api/auth
// #desc: Login
// #access: Public
router.post("/",
[
check("email", "Email is required").not().isEmpty(),
check("password", "Password is required").not().isEmpty(),
],
login
);
module.exports = router;
The Auth controller in controllers/authController.js :
const { validationResult } = require("express-validator");
const bcrypt = require("bcryptjs");
const jwt = require("jsonwebtoken");
require('dotenv').config()
exports.login = async (req, res) => {
try {
// Check for validation errors
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
// Check if user exists
const { email, password } = req.body;
const user = await User.findOne({ email });
if (!user) {
return res.status(400).json({ msg: "Invalid credentials : you must register" });
}
// If exists, check password
const isMatch = await bcrypt.compare(password, user.password);
if (!isMatch) {
return res.status(400).json({ msg: "Invalid credentials : wrong password" });
}
// Return jwt
const payload = {
user: {
id: user.id,
},
};
jwt.sign(payload,
process.env.JWT_SECRET,{expiresIn: 3600},(err, token) => {
if (err) throw err;
res.json({ token });
}
);
} catch (err) {
console.error(err.message);
res.status(500).send("There was an error with the server. Try again later.");
}
}
Everything looks great and when I try to login with postman it returns the "Invalid credentials : you must register" message from this code in authController.js ( when I console.log(user) it returns null ) :
const { email, password } = req.body;
const user = await User.findOne({ email });
if (!user) {
return res.status(400).json({ msg: "Invalid credentials : you must register" });
}
PS: At first, in the db configuration I forgot to add the mongo client options and everything worked and it returns the token in postman but after fixing the error I had the login problem
Goal - creating middleware-like callback in userHandler in util.js util.js for some express routes in express app, generated with express-generator and sequlize-cli.
Expected - user model successfully used in routes and userHandler in util.js.
Result - user model successfully works in login route, but is undefined in userHandler in util.js. When requiring models fully getting {}. Require seems correct.
console error
[nodemon] starting `node ./bin/www`
{}
Fri, 15 Jan 2021 14:54:42 GMT server:server Listening on port 8000
here
Fri, 15 Jan 2021 15:53:33 GMT server:server TypeError: Cannot read property 'findByPk' of undefined
at C:\Users\bpali\Desktop\authorization1\server\utils.js:40:27
at processTicksAndRejections (internal/process/task_queues.js:97:5)
at async C:\Users\bpali\Desktop\authorization1\server\utils.js:12:5
GET /users/my-info 500 23.306 ms - 2
File structure
util.js
const jwt = require('jsonwebtoken');
var debug = require('debug')('server:server');
const secret = process.env.SECRET || SgxLbntQGfukf1yEAYZyzLBJKvop8Z2k6q1YG0wOHXZMgbVNhhsLaSIOB8Rkw9Pe4anRpTAE8h95RCwDYLjSsbJhpdZruVKzpzGgw1bnqCLLS1gAU1R1uChtRYDkSBOoZkFdnG3sRVLtJz63gqEQkq7owtaACYOXAd1fPOFkUgwgxammROY1NgpmUJR9YOwhVtcq5mTu;
const { User } = require('./models');
const models = require('./models');
console.log(models); // {} - ???
const verifyToken = async (token) => new Promise((resolve, reject) => jwt.verify(token, secret, (err, decoded) => err ? reject(err) : resolve(decoded)));
const signToken = async (payload, options = { expiresIn: '365d' }) => new Promise((resolve, reject) => jwt.sign(payload, secret, options, (err, token) => err ? reject(err) : resolve(token)));
const asyncErrorHandler = (asyncFn, ...args) => async (req, res, next) => {
try {
await asyncFn(req, res, next, ...args);
next();
} catch (e) {
debug(e);
res.status(500).send(e);
}
}
const userHandler = (allowedTypes) => asyncErrorHandler(async (req, res, next) => {
const autHeader = req.get('Authorization');
if (!autHeader) {
res.status(401).send('Unauthorized');
next('route');
}
const autSplit = autHeader.split && autHeader.split(' ');
if (!autSplit || autSplit.length !== 2 || !(autSplit[0] === 'Token' || autSplit[0] === 'Bearer')) {
res.status(401).send('Wrong format of authorisation');
next('route');
}
const token = autSplit[1];
console.log('here');
let decodedToken;
try {
decodedToken = await verifyToken(token);
} catch (e) {
res.status(403).send(`Wrong token! ${e.name + ': ' + e.message}`);
next('route');
}
const { login } = decodedToken;
const user = await User.findByPk(login);
if (!user) {
res.status(403).send('Wrong user');
next('route');
}
req.user = user;
if (allowedTypes && allowedTypes.indexOf && allowedTypes.indexOf(user.role) === -1) {
res.status(403).send('Not allowed role');
next('route');
}
next();
});
module.exports = { verifyToken, signToken, asyncErrorHandler, userHandler }
routes/users.js
var express = require('express');
var router = express.Router();
const { QueryTypes } = require('sequelize');
const { User, sequelize } = require('../models');
const { asyncErrorHandler, userHandler } = require('../utils');
/* GET users listing. */
// router.get('/', function(req, res, next) {
// res.send('respond with a resource');
// });
router.post('/login', asyncErrorHandler(async function (req, res, next) {
const { login, password } = req.body;
if (!login || !password || typeof login !== 'string' || typeof password !== 'string'){
res.status(400).send('Not empty login and password strings needed in json body');
next();
}
const user = await User.findByPk(login);
if (!user){
res.status(400).send('Absent login');
next('route');
}
if (!(await user.checkPassword(password))){
res.status(400).send('Wrong password');
next('route');
};
const { type } = user
res.json({
token: await user.makeToken(),
user: { login, type },
});
// next();
}));
router.post('/register', asyncErrorHandler(async function (req, res, next) {
const { login, password } = req.body;
if (!login || !password || typeof login !== 'string' || typeof password !== 'string'){
res.status(400).send('Not empty login and password strings needed in json body');
next('route');
}
if (await User.findByPk(login)) {
res.status(403).send('Busy login');
next('route');
}
const user = User.build({ login, type: 'user' });
await user.setPassword(password);
await user.save();
const { type } = user
res.json({ login, type });
// next();
}));
router.get('/my-info', userHandler(), function (req, res, next) {
const { login, type } = req.user;
res.json({ login, type });
});
router.get('/user-info/:login', userHandler(['admin']), asyncErrorHandler(async function (req, res, next) {
const user = await (User.findByPk(req.params.login));
if (!user) {
res.status(404).send('Login not found');
next('route');
}
const { login, type } = user;
req.json({ login, type });
// next();
}));
router.get('/list-users', userHandler(['admin']), asyncErrorHandler(async function (req, res, next) {
const users = await sequelize.query("SELECT * FROM `users`", { type: QueryTypes.SELECT });
console.log(users);
res.json(users);
}));
module.exports = router;
models/index.js
'use strict';
const fs = require('fs');
const path = require('path');
const Sequelize = require('sequelize');
const basename = path.basename(__filename);
const env = process.env.NODE_ENV || 'development';
const config = require(__dirname + '/../config/config.json')[env];
const db = {};
let sequelize;
if (config.use_env_variable) {
sequelize = new Sequelize(process.env[config.use_env_variable], config);
} else {
sequelize = new Sequelize(config.database, config.username, config.password, config);
}
fs
.readdirSync(__dirname)
.filter(file => {
return (file.indexOf('.') !== 0) && (file !== basename) && (file.slice(-3) === '.js');
})
.forEach(file => {
const model = require(path.join(__dirname, file))(sequelize, Sequelize.DataTypes);
db[model.name] = model;
});
Object.keys(db).forEach(modelName => {
if (db[modelName].associate) {
db[modelName].associate(db);
}
});
db.sequelize = sequelize;
db.Sequelize = Sequelize;
module.exports = db;
models/user.js
'use strict';
const {
Model
} = require('sequelize');
const bcrypt = require('bcrypt');
const { signToken } = require('../utils');
module.exports = (sequelize, DataTypes) => {
class User extends Model {
/**
* Helper method for defining associations.
* This method is not a part of Sequelize lifecycle.
* The `models/index` file will call this method automatically.
*/
static associate(models) {
// define association here
}
async setPassword (password) {
this.password = await bcrypt.hash(password, 10);
}
async checkPassword (password) {
return await bcrypt.compare(password, this.password);
}
async makeToken() {
const { login } = this;
return await signToken({ login });
}
};
User.init({
login: {
type: DataTypes.STRING,
primaryKey: true,
allowNull: false,
},
password: {
type: DataTypes.STRING,
allowNull: false,
},
type: {
type: DataTypes.STRING,
allowNull: false,
},
}, {
sequelize,
modelName: 'User',
});
return User;
};
app.js
var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');
var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');
var app = express();
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'hbs');
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use('/', indexRouter);
app.use('/users', usersRouter);
// catch 404 and forward to error handler
app.use(function(req, res, next) {
next(createError(404));
});
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
module.exports = app;
bin/www
#!/usr/bin/env node
require('dotenv').config()
/**
* Module dependencies.
*/
var app = require('../app');
var debug = require('debug')('server:server');
var http = require('http');
/**
* Get port from environment and store in Express.
*/
var port = normalizePort(process.env.PORT || '3000');
app.set('port', port);
/**
* Create HTTP server.
*/
var server = http.createServer(app);
/**
* Listen on provided port, on all network interfaces.
*/
server.listen(port);
server.on('error', onError);
server.on('listening', onListening);
/**
* Normalize a port into a number, string, or false.
*/
function normalizePort(val) {
var port = parseInt(val, 10);
if (isNaN(port)) {
// named pipe
return val;
}
if (port >= 0) {
// port number
return port;
}
return false;
}
/**
* Event listener for HTTP server "error" event.
*/
function onError(error) {
if (error.syscall !== 'listen') {
throw error;
}
var bind = typeof port === 'string'
? 'Pipe ' + port
: 'Port ' + port;
// handle specific listen errors with friendly messages
switch (error.code) {
case 'EACCES':
console.error(bind + ' requires elevated privileges');
process.exit(1);
break;
case 'EADDRINUSE':
console.error(bind + ' is already in use');
process.exit(1);
break;
default:
throw error;
}
}
/**
* Event listener for HTTP server "listening" event.
*/
function onListening() {
var addr = server.address();
var bind = typeof addr === 'string'
? 'pipe ' + addr
: 'port ' + addr.port;
debug('Listening on ' + bind);
}
package.json
{
"name": "server",
"version": "0.0.0",
"private": true,
"scripts": {
"start": "node ./bin/www",
"dev": "nodemon ./bin/www"
},
"dependencies": {
"bcrypt": "^5.0.0",
"cookie-parser": "~1.4.4",
"debug": "~2.6.9",
"dotenv": "^8.2.0",
"express": "~4.16.1",
"hbs": "~4.0.4",
"http-errors": "~1.6.3",
"jsonwebtoken": "^8.5.1",
"morgan": "~1.9.1",
"pg": "^8.5.1",
"pg-hstore": "^2.3.3",
"sequelize": "^6.3.5"
},
"devDependencies": {
"nodemon": "^2.0.7"
}
}
.env
DEBUG=server:*
PORT=8000
SECRET=SgxLbntQGfukf1yEAYZyzLBJKvop8Z2k6q1YG0wOHXZMgbVNhhsLaSIOB8Rkw9Pe4anRpTAE8h95RCwDYLjSsbJhpdZruVKzpzGgw1bnqCLLS1gAU1R1uChtRYDkSBOoZkFdnG3sRVLtJz63gqEQkq7owtaACYOXAd1fPOFkUgwgxammROY1NgpmUJR9YOwhVtcq5mTu
Problem found - unclear circular dependency
utils.js requires models/index.js
models/index.js not directly requires models/user.js
models/user.js requires utils.js, more exactly - signToken function from it
What was done - renamed utils.js in routeUtils.js, put signToken and verifyToken functions to new file tokenUtils.js, updated imports in other files, so this circular dependency will not happen, after this, it imported correctly.
Main updates:
File structure
routeUtils.js
// const jwt = require('jsonwebtoken');
var debug = require('debug')('server:server');
// const secret = process.env.SECRET || SgxLbntQGfukf1yEAYZyzLBJKvop8Z2k6q1YG0wOHXZMgbVNhhsLaSIOB8Rkw9Pe4anRpTAE8h95RCwDYLjSsbJhpdZruVKzpzGgw1bnqCLLS1gAU1R1uChtRYDkSBOoZkFdnG3sRVLtJz63gqEQkq7owtaACYOXAd1fPOFkUgwgxammROY1NgpmUJR9YOwhVtcq5mTu;
const { User } = require('./models');
const { verifyToken } = require('./tokenUtils');
// const models = require('./models');
// console.log(models); // {} - ???
// const verifyToken = async (token) => new Promise((resolve, reject) => jwt.verify(token, secret, (err, decoded) => err ? reject(err) : resolve(decoded)));
// const signToken = async (payload, options = { expiresIn: '365d' }) => new Promise((resolve, reject) => jwt.sign(payload, secret, options, (err, token) => err ? reject(err) : resolve(token)));
const asyncErrorHandler = (asyncFn, ...args) => async (req, res, next) => {
try {
await asyncFn(req, res, next, ...args);
// next();
} catch (e) {
debug(e);
res.status(500).send(e);
}
}
const userHandler = (allowedTypes) => asyncErrorHandler(async (req, res, next) => {
const autHeader = req.get('Authorization');
if (!autHeader) {
res.status(401).send('Unauthorized');
next('route');
}
const autSplit = autHeader.split && autHeader.split(' ');
if (!autSplit || autSplit.length !== 2 || !(autSplit[0] === 'Token' || autSplit[0] === 'Bearer')) {
res.status(401).send('Wrong format of authorisation');
next('route');
}
const token = autSplit[1];
// console.log('here');
let decodedToken;
try {
decodedToken = await verifyToken(token);
} catch (e) {
debug(e);
res.status(403).send(`Wrong token! ${e.name + ': ' + e.message}`);
next('route');
}
const { login } = decodedToken;
const user = await User.findByPk(login);
if (!user) {
res.status(403).send('Wrong user');
next('route');
}
req.user = user;
if (allowedTypes && allowedTypes.indexOf && allowedTypes.indexOf(user.type) === -1) {
// console.log('here');
res.status(403).send('Not allowed user type');
next('route');
}
next();
});
module.exports = { /* verifyToken, signToken, */ asyncErrorHandler, userHandler }
tokenUtils.js
const jwt = require('jsonwebtoken');
const secret = process.env.SECRET || SgxLbntQGfukf1yEAYZyzLBJKvop8Z2k6q1YG0wOHXZMgbVNhhsLaSIOB8Rkw9Pe4anRpTAE8h95RCwDYLjSsbJhpdZruVKzpzGgw1bnqCLLS1gAU1R1uChtRYDkSBOoZkFdnG3sRVLtJz63gqEQkq7owtaACYOXAd1fPOFkUgwgxammROY1NgpmUJR9YOwhVtcq5mTu;
const verifyToken = async (token) => new Promise((resolve, reject) => jwt.verify(token, secret, (err, decoded) => err ? reject(err) : resolve(decoded)));
const signToken = async (payload, options = { expiresIn: '365d' }) => new Promise((resolve, reject) => jwt.sign(payload, secret, options, (err, token) => err ? reject(err) : resolve(token)));
module.exports = { verifyToken, signToken };
models/user.js
'use strict';
const {
Model
} = require('sequelize');
const bcrypt = require('bcrypt');
const { signToken } = require('../tokenUtils');
module.exports = (sequelize, DataTypes) => {
class User extends Model {
/**
* Helper method for defining associations.
* This method is not a part of Sequelize lifecycle.
* The `models/index` file will call this method automatically.
*/
static associate(models) {
// define association here
}
async setPassword (password) {
this.password = await bcrypt.hash(password, 10);
}
async checkPassword (password) {
return await bcrypt.compare(password, this.password);
}
async makeToken() {
const { login } = this;
return await signToken({ login });
}
};
User.init({
login: {
type: DataTypes.STRING,
primaryKey: true,
allowNull: false,
},
password: {
type: DataTypes.STRING,
allowNull: false,
},
type: {
type: DataTypes.STRING,
allowNull: false,
},
}, {
sequelize,
modelName: 'User',
});
return User;
};
problem: i keep getting the error in title when trying to register a new user only when using postman no idea why.
Also, Proxy problems: Could not proxy request /api/auth/register?hello&world from localhost:3000 to http://localhost:8080/.
Things i've tried to solve proxy was
adding "secure": false in package json.
my auth.js file:
exports.postRegister = async (req, res, next) => {
// User object
const newUser = {
username: req.body.username,
password: req.body.password,
};
// Check if user exists
try {
const user = await User.findOne({ username: newUser.username });
if (user) {
const error = new Error("Username already taken.");
error.statusCode = 422;
return next(error);
}
} catch (error) {
error.statusCode = error.statusCode || 500;
next(error);
}
// creating a hash from the given password
bcrypt.genSalt(12, (err, salt) => {
if (err) {
return next(err);
}
bcrypt.hash(newUser.password, salt, null, async (err, hash) => {
if (err) {
return next(err);
}
try {
// set hashed password as the password field in newUser object.
newUser.password = hash;
// Save newUser
const createdUser = new User(newUser);
await createdUser.save();
// Create jwt token and send
const token = await genToken(createdUser._id, createdUser.username);
res.status(201).json({
success: true,
token,
});
} catch (err) {
err.statusCode = err.statusCode || 500;
next(err);
}
});
});
};
Server.js file:
const express = require("express");
const mongoose = require("mongoose");
const bodyParser = require("body-parser");
const helmet = require("helmet");
const cors = require("cors");
require("dotenv").config();
const mailRoutes = require("./routes/mail");
const authRoutes = require("./routes/auth");
const app = express();
app.use(bodyParser.json());
app.use(helmet());
app.use(cors());
// Routes
app.use("/api/mail", mailRoutes);
app.use("/api/auth", authRoutes);
app.use((error, req, res, next) => {
console.log(error.message);
res.status(error.statusCode).json({
error: true,
messages: [error.message],
});
});
mongoose.connect(process.env.DB_URI, (err) => {
if (err) {
console.log(err);
} else {
console.log("DB Connected");
}
});
const PORT = process.env.PORT || 5000;
app.listen(PORT, () => {
console.log(`Server has started at ${PORT}`);
});
Ok so to anyone who is facing the same problem - i figured it out.
changing from arrow functions to regular function.
apparently bcrypt doesnt accept arrow functions
so instead of:
bcrypt.hash(newUser.password, salt, async => (err, hash){}...
do this:
bcrypt.hash(newUser.password, salt, async function (err, hash){}...
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...
I am trying to make a small authentication system for a practice project I have. I am trying to send POST requests via postman to my express server(http://localhost:4000/api/register), and it's replying back "404 not found" I am following the tutorial here.
Here is my server.js:
const newpost = require('./routes/newpost');
const getposts = require('./routes/getposts');
const express = require('express');
const bodyParser = require('body-parser');
const cors = require('cors');
const deleteposts = require('./routes/delete');
const editposts = require('./routes/editposts');
const path = require('path');
const app = express();
const webpack = require('webpack');
const webpackConfig = require('../webpack.config');
const webpackMiddleware = require('webpack-dev-middleware');
const webpackHotMiddleware = require('webpack-hot-middleware');
const passport = require('passport');
const port = 4000;
const compiler = webpack(webpackConfig);
const config = require('./config/main');
const mongoose = require('mongoose');
const authRouter = require('./routes/authrouter');
const logger = require('morgan');
// db connection
mongoose.connect(config.database);
app.use(passport.initialize());
/*
app.use(webpackMiddleware(compiler, {
noInfo: true, publicPath: webpackConfig.output.publicPath,
}));
app.use(webpackHotMiddleware(compiler, {
log: console.log,
}));
*/
app.use(cors());
// parse application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: false }));
// parse application/json
app.use(bodyParser.json());
app.use(logger('dev'));
app.use('/newpost', newpost);
app.use('/posts', getposts);
app.use('/delete', deleteposts);
app.use('/edit', editposts);
//auth router to handle auth routes
authRouter(app);
/*
app.get('/*', (req, res) => {
res.sendFile(path.join(__dirname, '../public/index.html'));
}); */
app.listen(port, () => {
console.log(`Listening on port ${port}`);
});
Here is my authrouter.js. These are the routes I'm exporting into my server.js. :
const AuthenticationController = require('../controllers/authentication');
const express = require('express');
// const passportService = require('../config/passport');
/* eslint-disable */
const passport = require('passport');
// Middleware to require login/auth
// const requireAuth = passport.authenticate('jwt', { session: false });
const requireLogin = passport.authenticate('local', { session: false });
module.exports = function(app){
"use-strict";
// init route groups
const apiRoutes = express.Router();
const authRoutes = express.Router();
// auth routes
// set auth routes as subgroup/middleware to apiRoutes
apiRoutes.use('/auth', authRoutes);
// Registration routes
authRoutes.post('/register', AuthenticationController.register);
// Login route
authRoutes.post('/login', requireLogin, AuthenticationController.login);
// Set url for API group routes
app.use('/api', apiRoutes);
};
Here is my authentication.js
const jwt = require('jsonwebtoken');
// const crypto = require('crypto'); used for pw resets
const User = require('../models/user');
const config = require('../config/main');
function generateToken(user) {
return jwt.sign(user, config.secret, {
expiresIn: 10080, // in seconds
});
}
// set user info from request
function setUserInfo(request) {
return {
_id: request._id,
email: request.email,
role: request.role,
};
}
// Login Route
exports.login = function (req, res, next) {
const userInfo = setUserInfo(req.user);
res.status(200).json({
token: `JWT${generateToken(userInfo)}`,
user: userinfo,
});
};
// registration route
exports.register = function (req, res, next) {
// check for registration errors
const email = req.body.email;
const password = req.body.password;
// Return error if no email provided
if (!email) {
return res.status(422).send({ error: 'You must enter an email address' });
}
// Return error if no pw provided
if (!password) {
return res.status(422).send({ error: 'You must enter a password' });
}
User.findOne({ email }, (err, existingUser) => {
if (err) { return next(err); }
// if user is not unique, return error
if (existingUser) {
return res.status(422).send({ error: 'That email address is already in use' });
}
// if email is unique and pw was provided, create acct
const user = new User({
email,
password,
});
user.save((err, user) => {
if (err) { return next(err); }
// Subscribe member to Mailchimp list
// mailchimp.subscribeToNewsLetter(user.email);
// Respond with JWT if user was created
const userInfo = setUserInfo(user);
res.status(201).json({
token: `JWT ${generateToken(userInfo)}`,
user: userInfo,
});
});
});
};
// Role authorization check
exports.roleAuthorization = function (role) {
return function (req, res, next) {
const user = req.user;
User.findById(user._id, (err, foundUser) => {
if (err) {
res.status(422).json({ error: 'No user was found' });
return next(err);
}
// if user is found, check role
if (foundUser.role == role) {
return next();
}
res.status(401).json({ error: 'You are not authorized to view this content ' });
return next('Unauthorized');
});
};
};
Here is my passport.js :
// Importing Passport; strategies; and config
const passport = require('passport');
const User = require('../models/user');
const config = require('./main');
const JwtStrategy = require('passport-jwt').Strategy;
const ExtractJwt = require('passport-jwt').ExtractJwt;
const LocalStrategy = require('passport-local');
const localOptions = { usernameField: 'email' };
// setting up the local Strategy
const localLogin = new LocalStrategy(localOptions, ((email, password, done) => {
User.findOne({ email }, (err, user) => {
if (err) { return done(err); }
if (!user) {
return done(null, false, { error: 'Your login details could not be verified. Please try again.',
});
}
user.comparePassword(password, (err, isMatch) => {
if (err) { return done(err); }
if (!isMatch) {
return done(null, false, { error: 'Your login details could not be verified. Please try again.',
});
}
return done(null, user);
});
});
}));
const jwtOptions = {
// Telling passport to check auth headers for JWT
jwtFromRequest: ExtractJwt.fromAuthHeader(),
// Telling passport where to find the secret
secretOrKey: config.secret,
};
// setting up JWT login strategy
const jwtLogin = new JwtStrategy(jwtOptions, ((payload, done) => {
User.findById(payload._id, (err, user) => {
if (err) { return done(err, false); }
if (user) {
done(null, user);
} else {
done(null, false);
}
});
}));
// allow passport to use the strategies we defined
passport.use(jwtLogin);
passport.use(localLogin);
I believe the correct post endpoint would be api/auth/register