the question is pretty self explanatory. I am registering/signing up users in a mongoDB database. They are being registered fine and an accesstoken [jwt based] is also being generated.
Now, when I go to query the database to fetch the list of users I am getting that error -
jwt is not defined.
It is worthwhile to mention that users also in my backend can have two type of roles - basic and admin. And only an admin user can fetch list of all users by sending accessToken in the header as Bearer authorization parameter.
I have 2 main files in my backend project structure that uses jwt.access methods like jwt.verify or jwt.signIn; these are the server.js and userController.js [a separate file where I have written all individual db related methods].
As far as I am concerned, all necessary packages are there in my project - express, node, jwa, jws, jsonwebtoken, mongo, mongoose, bcrypt, cors etc. So what is the trouble?
My route.js -->
const User = require('../models/user.model');
const jwt = require('jsonwebtoken');
const bcrypt = require('bcrypt');
const { roles } = require('../models/roles');
const JWT_SECRET = "$#GR24T4344$#$##%ETWWTEME%$6";
async function hashPassword(password) {
return await bcrypt.hash(password, 10);
}
async function validatePassword(plainPassword, hashedPassword) {
return await bcrypt.compare(plainPassword, hashedPassword);
}
exports.grantAccess = function (action, resource) {
return async (req, res, next) => {
try {
const permission = roles.can(req.user.role)[action](resource);
if (!permission.granted) {
return res.status(401).json({
error: "You don't have enough permission to perform this action"
});
}
next();
} catch (error) {
next(error);
}
}
}
exports.allowIfLoggedin = async (req, res, next) => {
try {
const user = res.locals.loggedInUser;
if (!user)
return res.status(401).json({
error: "You need to be logged in to access this route"
});
req.user = user;
next();
} catch (error) {
next(error);
}
}
exports.signup = async (req, res, next) => {
try {
const { role, email, password } = req.body;
const hashedPassword = await hashPassword(password);
const newUser = new User({ email, password: hashedPassword, role: role || "basic" });
const accessToken = jwt.sign({ userId: newUser._id }, JWT_SECRET, {
expiresIn: "1d"
});
newUser.accessToken = accessToken;
await newUser.save();
res.json({
data: newUser,
message: "You have signed up successfully"
});
} catch (error) {
next(error);
}
}
exports.login = async (req, res, next) => {
try {
const { email, password } = req.body;
const user = await User.findOne({ email });
if (!user)
return next(new Error('Email does not exist'));
const validPassword = await validatePassword(password, user.password);
if (!validPassword)
return next(new Error('Password is not correct'));
const accessToken = jwt.sign({ userId: user._id }, JWT_SECRET, {
expiresIn: "1d"
});
await User.findByIdAndUpdate(user._id, { accessToken });
res.status(200).json({
data: { email: user.email, role: user.role },
accessToken
});
} catch (error) {
next(error);
}
}
exports.getUsers = async (req, res, next) => {
const users = await User.find({});
res.status(200).json({
data: users
});
}
exports.getUser = async (req, res, next) => {
try {
const userId = req.params.userId;
const user = await User.findById(userId);
if (!user)
return next(new Error('User does not exist'));
res.status(200).json({
data: user
});
} catch (error) {
next(error);
}
}
exports.updateUser = async (req, res, next) => {
try {
const { role } = req.body;
const userId = req.params.userId;
await User.findByIdAndUpdate(userId, { role });
const user = await User.findById(userId);
res.status(200).json({
data: user
});
} catch (error) {
next(error);
}
}
exports.deleteUser = async (req, res, next) => {
try {
const userId = req.params.userId;
await User.findByIdAndDelete(userId);
res.status(200).json({
data: null,
message: 'User has been deleted'
});
} catch (error) {
next(error);
}
}
My server.js -->
const express = require('express');
const bodyParser = require('body-parser');
const cors = require('cors');
const PORT = 4000;
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const playerRoutes = express.Router();
const userRoutes = express.Router();
const userController = require('./controllers/userController');
const user_routes = require('./apiroutes/route');
const app = express();
const JWT_SECRET = "$#GR24T4344$#$##%ETWWTEME%$6";
const users = "users";
require("dotenv").config({path: __dirname+ '../.env'});
let Player = require('./models/player.model');
let User = require('./models/user.model');
app.use(cors());
app.use(bodyParser.json());
app.use(
bodyParser.urlencoded({
extended: false
})
);
mongoose.connect('mongodb://127.0.0.1:27017/playerDB', function (err, db) {
if (err)
throw err;
db.createCollection(users, function (err, resp) {
if (err)
throw err;
console.log("Collection created!");
});
}, { useNewUrlParser: true });
const connection = mongoose.connection;
connection.once('open', function () {
console.log("MongoDB database connection established successfully");
});
..... blablablaaaa
app.use('/playerDB', playerRoutes);
app.use(async (req, res, next) => {
res.header("Access-Control-Allow-Origin", "*");
if (req.headers["x-access-token"]) {
try {
const accessToken = req.headers["x-access-token"];
const { userId, exp } = await jwt.verify(accessToken, JWT_SECRET);
// If token has expired
if (exp < Date.now().valueOf() / 1000) {
return res.status(401).json({
error: "JWT token has expired, please login to obtain a new one"
});
}
res.locals.loggedInUser = await User.findById(userId);
next();
} catch (error) {
next(error);
}
} else {
next();
}
});
app.use('/users', user_routes);
app.listen(PORT, function () {
console.log("Server is running on Port: " + PORT);
});
I hope you understand my approach and scenario? Can you guess, where it could have gone wrong? Any idea?
Missing npm packages or something more critical?
Look forward to some hints on this problem! Can't seem to figure out a way!
it seems you forgot to add this line to server.js
const jwt = require('jsonwebtoken');
While register and login, this didn't caused a problem, because for these requests, req.headers["x-access-token"] was null, and the code didn't reach the if block where you used jwt, but one a request with this header came (like getUsers) the code tried to use jwt.verify, but since jwt wasn't imported it gave error.
Related
When making the middleware request in my route, I always fall into the else of "verifyAdmin" (error 403). The big problem is that I can't send a throw or catch of this error, it just doesn't return any error in the terminal, but when testing in postman it always goes to else
const jwt = require('jsonwebtoken');
const verifyToken = (req, res, next) => {
const { authorization } = req.headers;
if (!authorization) {
return res.status(401).json('Invalid Authorization')
};
const token = authorization.replace('Bearer', ' ').trim();
try {
const secret = process.env.JWT_SECRET;
const data = jwt.verify(token, secret);
req.users = data;
const { id } = data;
req.userId = id;
return next();
} catch (err) {
return res.status(400).json(err);
}
};
const verifyAdmin = (req, res, next) => {
if (req.users.isAdmin === true) {
next();
} else {
return res.status(403).json("You are not alowed to do that!");
}
};
module.exports = {
verifyToken,
verifyAdmin,
};
in route
const { verifyToken, verifyAdmin } = require('../middlewares/verifyToken');
router.get('/', verifyToken, verifyAdmin, FindAllUsersController.index);
construction token
const db = require('../../models/index');
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
exports.store = async (req, res) => {
const { email, password } = req.body;
const secret = process.env.JWT_SECRET;
try {
const user = await db.User.findOne({ where: { email } });
if (!user) {
return res.status(401).json('User does not exist');
}
const isValidPassword = await bcrypt.compare(password, user.password);
if (!isValidPassword) {
return res.status(401).json('Password is not valid');
}
const token = jwt.sign({ id: user.id }, secret, {
expiresIn: process.env.EXPIRES_TOKEN,
})
return res.status(200).json({
user,
token,
});
} catch (err) {
console.log(err);
}
}
The isAdmin flag is not contained in your token, because you include only the id when constructing it:
const token = jwt.sign({ id: user.id }, ...)
You need (at least):
const token = jwt.sign({ id: user.id, isAdmin: user.isAdmin }, ...)
So im trying to make an admin login and I can get it to hash the password. But when I update the password or even the username it will un-hash the passwor for me when i need it to stay hashed. When I check my MongoDB itll be un-hashed for some reason. Any help will be appreciated. Thank you.
This is my user route
var express = require("express");
var router = express.Router();
const mongoose = require("mongoose");
const bcrypt = require("bcryptjs");
const jwt = require("jsonwebtoken");
const { isAuthenticated } = require("../middleware/auth");
const Admin = require("../models/Admin");
const saltrounds = 10;
/* GET users listing. */
router.get("/", function (req, res, next) {
res.send("respond with a resource");
});
router.post("/login", async (req, res) => {
if (!req.body.username || !req.body.password) {
return res.json({ message: "Please enter username and password" });
}
try {
const foundUser = await Admin.findOne({ username: req.body.username });
if (!foundUser) {
return res
.status(400)
.json({ message: "Username or password is incorrect" });
}
const isMatch = bcrypt.compareSync(req.body.password, foundUser.password);
if (!isMatch) {
return res
.status(400)
.json({ message: "Username or password incorrect" });
}
const payload = {
username: foundUser.username,
id: foundUser._id,
};
const token = jwt.sign(payload, process.env.SECRET, {
algorithm: "HS256",
expiresIn: "6h",
});
res.json({ token: token, id: foundUser.id });
} catch (err) {
res.status(400).json(err.message);
}
});
router.post("/update", isAuthenticated, async (req, res) => {
try {
const updateUser = await Admin.findByIdAndUpdate(
req.user.id,
{ ...req.body },
{ new: true }
);
res.json(updateUser);
} catch (err) {
res.json(err.message);
}
});
module.exports = router;
This is my authenticated folder
const jwt = require("jsonwebtoken");
const isAuthenticated = async (req, res, next) => {
// const token = req.headers.authorization;
// NOTE: if your token authentication is failing in Postman, uncomment the line below, and comment out the line above
const token = req.headers.authorization?.split(" ")[1];
if (!token || token === "null") {
console.log("NO TOKEN");
return res.status(400).json({ message: "Token not found" });
}
try {
const tokenInfo = jwt.verify(token, process.env.SECRET);
console.log(tokenInfo);
//If you have req.payload, change line 12 to:
// req.payload = tokenInfo;
req.user = tokenInfo;
next();
} catch (error) {
return res.status(440).json(error);
}
};
// Export the middleware so that we can use it to create a protected routes
module.exports = {
isAuthenticated,
};
I think the issue is located at the (function)
Admin.findByIdAndUpdate
May be the find unhash the password fisrt and the update do not hash it again.
Check at this function.
So I create my route user and I use this tutorial https://www.bezkoder.com/node-js-jwt-authentication-mysql/ to add the token and Authentication Token + validation name. Everything work great. But then when I beginning to create my route Comment (acces for every user), I had normaly the auth but even with the token Postman send me an "No token provided!". I have to add this token and autorize the acces but I don't know how. If somebody know how to do it, it would be great :D thx ! This is how I add my token on Postman
enter image description here
Here is my code:
My route comment:
const express = require("express");
const router = express.Router();
const commentCtrl = require("../controllers/comment");
const { authJwt } = require("../middleware");
router.post("/upload", [authJwt.verifyToken], commentCtrl.createComment);
module.exports = router;
The middleware token :
const jwt = require("jsonwebtoken");
const config = require("../config/auth.config.js");
verifyToken = (req, res, next) => {
let token = req.headers["x-access-token"];
if (!token) {
return res.status(403).send({
message: "No token provided!"
});
}
jwt.verify(token, config.secret, (err, decoded) => {
if (err) {
return res.status(401).send({
message: "Unauthorized!"
});
}
req.id = decoded.id;
next();
});
};
const authJwt = {
verifyToken: verifyToken,
};
module.exports = authJwt;
const authJwt = require("./authJwt");
const verifySignUp = require("./verifySignUp");
module.exports = {
authJwt,
verifySignUp
};
My verification SignUp:
const { User } = require("../models");
checkDuplicateEmail = async (req, res, next) => {
const user = await User.findOne({
where: {
email: req.body.email
}
}).then(user => {
if (user) {
res.status(400).send({
message: "Failed! Email is already in use!"
});
return;
}
next();
});
};
const verifySignUp = {
checkDuplicateEmail: checkDuplicateEmail
};
module.exports = verifySignUp;
And here is my user.controllers when is send the token:
const bcrypt = require("bcrypt");
const jwt = require("jsonwebtoken");
const config = require("../config/auth.config");
const { User } = require("../models");
module.exports.signup = async (req, res) => {
if (!req.body.email || !req.body.password) {
res.status(400).send({
status: false,
message: 'une erreur dans create est survenu'
});
} else {
let { nom, prenom, email, password, status} = req.body;
const salt = await bcrypt.genSalt(10);
password = await bcrypt.hash(password, salt)
const user = await User.create({
nom, prenom, email, password, status
})
.then((user) => res.status(201).send(user)).catch((error) => {
console.log(error);
res.status(400).send(error);
});
}
};
module.exports.login = async (req, res) => {
const user = await User.findOne({
where: {
email: req.body.email
}
})
.then(user => {
if (!user) {
return res.status(404).send({ message: "User Not found." });
}
const passwordIsValid = bcrypt.compareSync(req.body.password, user.password);
if (!passwordIsValid) {
return res.status(401).send({
accessToken: null,
message: "Invalid Password!"
});
}
var token = jwt.sign({ id: user.id }, config.secret, {
expiresIn: 86400 // 24 hours
});
res.status(200).send({
id: user.id,
nom: user.nom,
email: user.email,
password: user.password,
accessToken: token
});
})
.catch(err => {
res.status(500).send({ message: err.message });
});
};
While it would have helped if you had provided a screenshot of where you are putting your token on postman, make sure you are using the appropriate header. According to your code, you are searching the 'x-access-token' header, so in postman, you should attach your jwt token to the same header property. It should be something like this:
postman x-access-token demo
When calling my PUT method, I got this error:
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html>
<head>
<title>403 Forbidden</title>
</head>
<body>
<h1>Forbidden</h1>
<p>You don't have permission to access this resource.</p>
</body>
</html>
I have some jwt authentification, so I put my complete code here:
It works with GET and POST methods
const express = require('express');
const logger = require('morgan');
var fs = require('fs');
var https = require('https');
var privateKey = fs.readFileSync('/etc/letsencrypt/live/hidden/privkey.pem', 'utf8');
var certificate = fs.readFileSync('/etc/letsencrypt/live/hidden/fullchain.pem', 'utf8');
const util = require('util')
var credentials = {key: privateKey, cert: certificate};
var queryString = require('querystring');
const bodyParser = require('body-parser');
const app = express();
const jwt = require('jsonwebtoken');
// import passport and passport-jwt modules
const passport = require('passport');
const passportJWT = require('passport-jwt');
const bcrypt = require('bcryptjs');
const Sequelize = require('sequelize');
var httpProxy = require('http-proxy');
var apiProxy = httpProxy.createProxyServer();
var backend = 'http://localhost:8484';
// initialize an instance of Sequelize
const sequelize = new Sequelize({
//hiden///
});
// ExtractJwt to help extract the token
let ExtractJwt = passportJWT.ExtractJwt;
// JwtStrategy which is the strategy for the authentication
let JwtStrategy = passportJWT.Strategy;
let jwtOptions = {};
jwtOptions.jwtFromRequest = ExtractJwt.fromAuthHeaderAsBearerToken();
jwtOptions.secretOrKey = 'hidden';
// lets create our strategy for web token
let strategy = new JwtStrategy(jwtOptions, function(jwt_payload, next) {
console.log('payload received', jwt_payload);
let user = getUser({ id: jwt_payload.id });
if (user) {
next(null, user);
} else {
next(null, false);
}
});
// use the strategy
passport.use(strategy);
app.use(passport.initialize());
app.use(logger(':date[iso] :method :url :status :response-time ms - :res[content-length]'));
// parse application/json
app.use(bodyParser.json());
//parse application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: true }));
// check the databse connection
sequelize
.authenticate()
.then(() => console.log('Connection has been established successfully.'))
.catch(err => console.error('Unable to connect to the database:', err));
// create user model
const User = sequelize.define('user', {
name: {
type: Sequelize.STRING,
allowNull: false
},
code_event: {
type: Sequelize.INTEGER,
allowNull: false
},
password: {
type: Sequelize.STRING,
allowNull: false
},
});
// create table with user model
User.sync()
.then(() => console.log('User table created successfully'))
.catch(err => console.log('did you enter wrong database credentials?'));
// create some helper functions to work on the database
const createUser = async ({ name, password, code_event }) => {
try{
return await User.create({ name, password, code_event });
}
catch (error) {
// your catch block code goes here
}
};
const getAllUsers = async () => {
try{
return await User.findAll();
}
catch (error) {
// your catch block code goes here
}
};
const getUser = async obj => {
try{
return await User.findOne({where: obj,});
}
catch (error) {
// your catch block code goes here
}
};
// get all users
app.get('/users', function(req, res) {
getAllUsers().then(user => res.json(user));
});
// register route
app.post('/register', function(req, res, next) {
console.log(req.body);
let { name, password, code_event } = req.body;
bcrypt.genSalt(10, (err, salt) => {
if(err) throw err;
bcrypt.hash(password, salt,
(err, hash) => {
if(err) throw err;
password = hash;
createUser({ name, password, code_event }).then(user =>
res.json({ user, msg: 'account created successfully' })
)
.catch(err => res.status(400).json(err));
});
});
});
// login route
app.post('/login', async function(req, res, next) {
console.log(req.body);
const { name, password , code_event} = req.body;
if (name && password) {
// we get the user with the name and save the resolved promise returned
let user = await getUser({ name, code_event});
if (!user) {
res.status(401).json({ msg: 'No such user found', user });
}
bcrypt.compare(password, user.password)
.then(isMatch => {
if (isMatch) {
const payload = {id: user._id};
// let token = jwt.sign(payload, jwtOptions.secretOrKey, { expiresIn: 36000 }, (err, token) => {
// if (err) res.status(500).json({ error: "Error signing token", raw: err });
// res.json({ msg: 'ok', token: token });
// });
let token = jwt.sign(payload, jwtOptions.secretOrKey);
res.json({ msg: 'ok', token: token });
} else {
res.status(401).json("Password is incorrect");
}
});
}
});
apiProxy.on( 'proxyReq', ( proxyReq, req, res, options ) => {
console.log("body " +util.inspect(req.body, false, null, true /* enable colors */));
if ( !req.body || !Object.keys( req.body ).length ) {
return;
}
let contentType = proxyReq.getHeader( 'Content-Type' );
let bodyData;
if ( contentType.includes( 'application/json' ) ) {
bodyData = JSON.stringify( req.body );
}
if ( contentType.includes( 'application/x-www-form-urlencoded' ) ) {
bodyData = queryString.stringify( req.body );
}
if ( bodyData ) {
proxyReq.setHeader( 'Content-Length', Buffer.byteLength( bodyData ) );
proxyReq.write( bodyData );
}
});
app.all("/*", passport.authenticate('jwt', { session: false }), function(req, res) {
console.log("req all" + req);
apiProxy.web(req, res, {target: backend});
});
app.on('upgrade', function (req, socket, head) {
console.log("req on" + req);
apiProxy.ws(req, socket, head, {target: backend});
});
var httpsServer = https.createServer(credentials, app);
// start the app
httpsServer.listen(8383, function() {
console.log("Express is running on port 3000");
});
It works with my POST methods.
I don't know where it goes because, when calling a method, I should have a trace in my log files. I can see POST ot GET calls but not PUT ones.
Any ideas?
I'm new to NodeJS and ExpressJS. I created an AuthController which handles the login post.
exports.login = async (req, res, next) => {
try {
const email = req.body.email;
const password = req.body.password;
const user = await User.findOne({ email }).select("+password");
if (!user) {
const error = new Error("Wrong Credentials");
error.statusCode = 401;
throw error;
}
const validPassword = await user.validPassword(password);
if (!validPassword) {
const error = new Error("Wrong Credentials");
error.statusCode = 401;
throw error;
}
const token = jwt.encode({id: user.id}, config.jwtSecret);
res.send({user, token});
} catch(err) {
next(err);
}
}
I would like to use this method in my web application to let the user login. If the user succesfully logs in, I get a json response in my browser with the user and the token. That's not what I want. The url for the api login is: /api/auth/login. I created a function in my web application:
router.post('/login', async (req, res, next) => {
var apiResponse = await authController.login(req, res, next);
res.render('profile.ejs', {user: apiResponse.user});
});
I keep getting the error that authController.login is not a function. My question is how can I use the method from my API and use the response in my webapplication? Can someone help me out?
You can do it like this code below: 👇
router.post('/login', authController.login, async (req, res, next) => {
res.render('profile.ejs', {user: apiResponse.user});
});
👨🏫 Please read the documentation about Express Middleware.
Updated: Middleware
Make sure your authController looks like this code below:
exports.login = async (req, res, next) => {
try {
const email = req.body.email;
const password = req.body.password;
const user = await User.findOne({ email }).select("+password");
if (!user) {
const error = new Error("Wrong Credentials");
error.statusCode = 401;
throw error;
}
const validPassword = await user.validPassword(password);
if (!validPassword) {
const error = new Error("Wrong Credentials");
error.statusCode = 401;
throw error;
}
const token = jwt.encode({id: user.id}, config.jwtSecret);
// passing your data to next function
// you can change exampleData with the 'variable' you want
req.exampleData = { user, token }
next();
} catch(err) {
next(err);
}
}
If your authController is same root ("folder") with the your routes, than make it's look like this:
const { login } = require('./authController');
So, now, you can call the authController like this:
router.post('/login', login, async (req, res, next) => {
// your data: "user" and "token" from middleware
const { exampleData } = req;
console.log(exampleData);
res.render('profile.ejs', {user: exampleData});
});
I hope it's can help you 🙏.