I tried to add passport-jwt to my MEVN-stack application, login is successful, but when app tries to redirect to home page after login, I get 401 Unauthorized error in console. I pass token as value of Authorization header in get request on the home page, but it did not help.
This is the code of server.js (the entrypoint of server side):
const express = require('express');
const bodyParser = require('body-parser');
const mongoose = require('mongoose');
const cors = require('cors');
const morgan = require('morgan');
const fs = require('fs');
const jwt = require('jsonwebtoken');
const passport = require('passport');
const passportJWT = require('passport-jwt');
const ExtractJWT = passportJWT.ExtractJwt;
const JWTStrategy = passportJWT.Strategy;
const jwtOptions = {};
jwtOptions.jwtFromRequest = ExtractJWT.fromAuthHeaderWithScheme('jwt');
jwtOptions.secretOrKey = 'movieratingapplicationsecretkey';
const app = express();
const router = express.Router();
const User = require('./models/User');
app.use(morgan('combined'));
app.use(bodyParser.json());
app.use(cors());
app.use(passport.initialize());
passport.use(new JWTStrategy(jwtOptions, (jwt_payload, done) => {
User.findOne({ id: jwt_payload.id }, (err, user) => {
if (err) {
return done(err, false);
}
if (user) {
return done(null, user);
} else {
return done(null, false);
}
});
}));
mongoose.connect('mongodb://localhost/movie_rating_app', {
useNewUrlParser: true,
useUnifiedTopology: true,
})
.then(() => {
console.log('Connection is established');
})
.catch((err) => {
console.error(`App starting error: ${err.stack}`);
});
// include controllers
fs.readdirSync('controllers').forEach(file => {
if (file.substr(-3) === '.js') {
const route = require(`./controllers/${file}`)
route.controller(app)
}
})
router.get('/', (req, res) => {
res.json({ message: 'API was initialized!' });
});
const port = process.env.API_PORT || 8081;
app.use('/', router);
app.listen(port, () => {
console.log(`api running on port ${port}`);
});
This is the movies.js controllerm, which contains passport.authenticate() method in get request:
const Movie = require('../models/Movie')
const Rating = require('../models/Rating')
const passport = require('passport')
module.exports.controller = app => {
// fetch all movies
app.get(
'/movies',
passport.authenticate('jwt', { session: false }),
(req, res) => {
Movie.find({}, 'name description release_year genre', (error, movies) => {
if (error) console.error(error)
res.send(movies);
})
})
// fetch a single movie
app.get('/movies/:id', (req, res) => {
Movie.findById(req.params.id, 'name description release_year genre', (error, movie) => {
if (error) console.error(error)
res.send(movie);
})
})
}
This is the users.js controller:
const User = require('../models/User');
const passportJWT = require('passport-jwt');
const jwt = require('jsonwebtoken');
const ExtractJwt = passportJWT.ExtractJwt;
const jwtOptions = {};
jwtOptions.jwtFromRequest = ExtractJwt.fromAuthHeaderWithScheme('jwt');
jwtOptions.secretOrKey = 'thisisthesecretkey';
module.exports.controller = app => {
// register a user
app.post('/users/register', (req, res) => {
const newUser = new User({
name: req.body.name,
email: req.body.email,
password: req.body.password,
})
User.createUser(newUser, (error, user) => {
if (error) {
res.status(422).json({
message: 'Something went wrong. Please try again after some time'
})
}
res.send({ user });
})
})
// login user
app.post('/users/login', (req, res) => {
if (req.body.email && req.body.password) {
const email = req.body.email,
password = req.body.password;
User.getUserByEmail(email, (err, user) => {
if (!user) {
res.status(404).json({ message: 'The user does not exist' })
} else {
User.comparePassword(password, user.password, (error, isMatch) => {
if (error) throw error;
if (isMatch) {
const payload = { id: user.id };
const token = jwt.sign(payload, jwtOptions.secretOrKey);
res.json({ message: 'ok', token })
} else {
res.status(401).json({ message: 'The password is incorrect' })
}
})
}
})
}
})
}
This is the script part of Home.vue, when I try to receive response:
<script>
import axios from 'axios';
import MovieCard from '#/components/MovieCard.vue';
export default {
name: 'Home',
components: {
MovieCard,
},
data: () => ({
movies: [],
}),
mounted() {
this.fetchMovies();
},
methods: {
async fetchMovies() {
const token = window.localStorage.getItem('auth');
return axios({
method: 'get',
url: 'http://localhost:8081/movies',
headers: {
Authorization: `JWT ${token}`,
'Content-Type': 'application/json',
},
})
.then((response) => {
console.log(response);
});
// return axios.get('http://localhost:8081/movies')
// .then(({ data }) => {
// this.movies = data;
// })
// .catch((error) => {
// console.error(error);
// });
},
},
};
</script>
Please don't mark my question as duplicate, because I have already tried some advices such as change ExtractJWT.fromAuthHeaderWithScheme('jwt') to ExtractJWT.fromAuthHeaderWithScheme('bearer'), but it did not help me.
How to fix 401 error?
I replaced all ExtractJwt.fromAuthHeaderWithScheme('jwt') with ExtractJwt.fromAuthHeaderAsBearerToken(), and set all jwtOptions.secretOrKey values as 'thisisthesecretkey'. 401 error does not exist now
Related
I am trying postman for signup a user, {"firstName": "John", "lastName":"zoe", "email":"aaa#gmail.com", "password":"123465"} but the postman gives me this 500 error: {
"message": "Something went wrong"
},
I could not figure out is my logic wrong, or something is missing, I did not use the validator package, as I am not sure how to use it, is that the problem? can anyone pls help?
here is my code, in the server.js file:
const express = require("express");
const env = require("dotenv");
const { response } = require("express");
const app = express();
const mongoose = require("mongoose");
//routes
const authRoutes = require("./routes/auth");
const adminRoutes = require("./routes/adminauth");
const categoryRoutes = require("./routes/category");
//enviorment variables
env.config();
app.use(express.json());
mongoose
.connect(
`mongodb+srv://${process.env.MONGO_DB_USER}:${process.env.MONGO_DB_PASSWORD}#cluster0.h28xczp.mongodb.net/${process.env.MONGODB_DATABASE}?retryWrites=true&w=majority`
)
.then(() => {
console.log("Database connection established");
});
app.use("/api", authRoutes);
app.use("/api", adminRoutes);
app.use("/api", categoryRoutes);
app.listen(process.env.PORT, () => {
console.log(`server is running at ${process.env.PORT}`);
});
In my routes file:
const express = require("express");
const router = express.Router();
const { signupUser, loginUser } = require("../controller/auth");
const { auth, userMiddleware, adminMiddleware } = require("../middleware/auth");
//login route
router.post("/login", loginUser);
//signup route
router.post("/signup", signupUser);
module.exports = router;
Middleware file:
const jwt = require("jsonwebtoken");
const User = require("../models/user");
exports.auth = (req, res, next) => {
try {
const token = req.header.authorization.split("")[1];
const isCustomAuth = token.length < 500;
let decodeData;
if (token && isCustomAuth) {
decodeData = jwt.verify(token, env.Process.JWT_SECRET);
req.UserId = decodeData?.id;
} else {
decodeData = jwt.decode(token);
req.UserId = decodeData?.sub;
}
next();
} catch (error) {}
};
exports.userMiddleware = (req, res, next) => {
if (req.user.role !== "user") {
return res.status(400).json({ message: "User access denied" });
}
next();
};
exports.adminMiddleware = (req, res, next) => {
if (req.user.role !== "admin") {
return res.status(400).json({ message: "Access denied" });
}
next();
};
In my controller file:
const User = require("../models/user");
const jwt = require("jsonwebtoken");
const bcrypt = require("bcrypt");
exports.loginUser = async (req, res) => {
const { email, password } = req.body;
try {
const existingUser = await User.findOne({ email });
if (!existingUser)
return res.status(400).json({ message: "User does not exists." });
const isPasswordCorrect = await bcrypt.compare(
password,
existingUser.password
);
if (!isPasswordCorrect)
return res.status(400).json({ message: "Invalid credentials." });
const token = jwt.sign(
{ email: existingUser.email, id: existingUser._id },
process.env.JWT_SECRET,
{ expiresIn: "3d" }
);
res.status(200).json({ result: existingUser, token });
} catch (error) {
res.status(500).json({ message: "Something went wrong" });
}
};
exports.signupUser = async (req, res) => {
const { firstName, lastName, email, password, confirmPassword } = req.body;
try {
const existingUser = await User.findOne({ email });
if (existingUser)
return res.status(400).json({ message: "User already exists." });
if (!password == confirmPassword)
return res.status(400).json({ message: "Password don't match" });
const hashedPassword = await bcrypt.hash(password, 12);
const result = await User.create({
email,
password: hashedPassword,
firstName,
lastName,
});
const token = jwt.sign(
{ email: result.email, id: result._id },
process.env.JWT_SECRET,
{ expiresIn: "3d" }
);
res.status(200).json({ result, token });
} catch (error) {
res.status(500).json({ message: "Something went wrong" });
}
};
My user model file:
const mongoose = require("mongoose");
const bcrypt = require("bcrypt");
const userSchema = new mongoose.Schema(
{
firstName: {
type: String,
required: true,
trim: true,
},
lastName: {
type: String,
required: true,
trim: true,
},
email: {
type: String,
required: true,
trim: true,
unique: true,
},
password: {
type: String,
required: true,
},
id: {
type: String,
},
},
{ timestamps: true }
);
module.exports = mongoose.model("User", userSchema);
In the middleware, this line contains a wrong reference to JWT_SECRET.
decodeData = jwt.verify(token, env.Process.JWT_SECRET);
Should be
decodeData = jwt.verify(token, process.env.JWT_SECRET);
The application throws an unhandled promise rejection error when trying to connect DB, which means it can operate without a DB connection and then throw that error.
So, to handle that, you can rewrite your code to this.
mongoose.connect('mongodb://localhost:27017/usersdb', // change with your db url
{
useNewUrlParser: true,
useUnifiedTopology: true
}
)
.then(() => {
app.use("/api", authRoutes);
app.listen(process.env.PORT, () => {
console.log("Server has started on port!", process.env.PORT)
})
})
.catch(() => { throw new Error(("Connection error")) });
Also, I successfully ran and tested your application on my local machine. Here is a GitHub link; you can compare.
https://github.com/nairi-abgaryan/express-auth
I have made an authentication with jwt api in node.js/express and i run it on heroku.
When a user logged in, server create a cookie via cookie-parser and send it to the client.
Below is the code from server.js
const express = require('express');
const jwt = require('jsonwebtoken');
const cookieParser = require('cookie-parser');
const cors = require('cors');
const path = require('path');
const bcrypt = require('bcrypt');
const PORT = process.env.PORT || 3000;
const serveStatic = require('serve-static');
require('dotenv').config();
const mongoose = require('mongoose');
const { User, Posts } = require(path.join(__dirname, './model.js'));
const mongoString = process.env.DATABASE_URL;
const JWT_SECRET = process.env.JWT_SECRET;
const { verifyToken, checkUser } = require(path.join(__dirname, './auth.js'));
const app = express();
//Middlewares
app.use(
cors({
credentials: true,
origin: true,
})
);
app.use(express.json());
app.use(cookieParser());
//Connect to Database
mongoose.connect(mongoString);
const db = mongoose.connection;
db.on('error', (err) => {
console.log(err);
});
db.once('connected', () => {
console.log('----Database Connected----\n');
});
//functions
const maxAge = 3 * 24 * 60 * 60;
const createToken = (id) => {
return jwt.sign({ id }, JWT_SECRET, {
expiresIn: maxAge,
});
};
// AUTH ROUTES
app.get('*', checkUser);
app.get('/', checkUser, (req, res) => {
res.json({ status: 'success' });
});
app.post('/api/register', async (req, res) => {
const salt = await bcrypt.genSalt();
try {
const user = await User.create(
new User({
username: req.body.username,
email: req.body.email,
city: req.body.city,
password: await bcrypt.hash(req.body.password, salt),
})
);
const token = createToken(user._id);
res.cookie('jwt', token, {
maxAge: maxAge * 1000,
secure: true,
});
res.status(201).json(user);
console.log(user);
} catch (err) {
console.log(err);
res.json(err);
}
});
app.post('/api/login', async (req, res) => {
try {
const { email, password } = req.body;
const user = await User.findOne({ email }).lean();
if (!user) {
return res.send({
status: 'error',
error: 'Invalid email',
});
}
if (await bcrypt.compare(password, user.password)) {
const token = createToken(user._id);
res.cookie('jwt', token, { secure: true, maxAge: maxAge * 1000 });
res.status(200).send({ status: 'ok', token: token });
console.log(user._id + ' logged in successfully');
return;
}
return res.send({ status: 'error', error: 'Invalid password' });
} catch (err) {
console.log(err);
}
});
app.get('/api/home', verifyToken, (req, res) => {
res.send(res.locals.user);
});
app.get('/api/logout', (req, res) => {
try {
res.cookie('jwt', '', { maxAge: 1 });
res.status(200).send({ status: 'ok' });
} catch (err) {
res.send(err);
}
});
//POSTS ROUTES
app.post('/api/posts', verifyToken, checkUser, async (req, res) => {
try {
const post = await Posts.create(
new Posts({
postBody: req.body.postBody,
city: req.body.city,
author: res.locals.user.id,
})
);
res.status(200).json(post);
console.log('====New Post=====');
} catch (err) {
res.status(400).send({ message: err.message });
}
});
app.get('/api/posts', verifyToken, async (req, res) => {
try {
const data = await Posts.find();
res.send({ user: res.locals.user, data: data });
} catch (err) {
res.json({ message: err.message });
}
});
app.get('/api/posts/:city', verifyToken, async (req, res) => {
try {
const data = await Posts.find({ city: req.params.city });
res.json(data);
res.send(res.locals.user);
} catch (err) {
res.json({ message: err.message });
}
});
//run server
app.listen(PORT, () => {
console.log(`Server running on ${PORT}...\n`);
});
Now, for front-end i use Vue.js that its running on Firebase.
Here is the script part of Login.Vue
<script>
/* eslint-disable */
import axios from 'axios';
export default {
name: 'Login',
data() {
return {
email: '',
password: '',
error: '',
};
},
methods: {
async onSubmit() {
if (!this.email || this.password.length < 6) {
this.error = 'vale kati';
return;
}
await axios
.post(
'https://thelostpet.herokuapp.com/api/login',
{
email: this.email,
password: this.password,
},
{ withCredentials: true }
)
.then((res) => {
console.log(res.data.token);
if (res.data.status == 'error') {
this.error = res.data.error;
}
if (res.data.status == 'ok') {
this.$router.push('/home');
}
})
.catch((err) => {
console.log(err);
});
},
},
};
</script>
When I try to login from Login.vue, that its ruuning on Firebase, the browser doesn't save the cookie that it created from the api.
BUT when I make a post request on https://thelostpet.herokuapp.com/api/login from postman, the cookie is saved on postman.
Thank you!
When signing in with postman everything works fine. But when i am doing an axios request i get 404 error and directly after 204 error. When i render my vue.js page i get "cannot get api/auth/signin. Also I get a message somewhere that says user not found.
What i have tried:
Frontend: I tried with adding headers to my axios request. I console logged the data and it seems perfectly fine.
Backend: Changed deprecated body parsers.
Frontend Code:
Auth store
import axios from "axios";
const state = {
token: "",
users: [],
};
const getters = {};
const actions = {
async signIn(_, payload) {
const response = await axios.post(
"http://localhost:3000/api/auth/signin",
{ payload },
{
headers: {
"Content-Type": "application/json",
},
}
);
console.log(response.data);
console.log(response.headers);
console.log(response.status);
},
};
const mutations = {};
export default {
state,
getters,
actions,
mutations,
};
This is my backend:
Controller
//signin
exports.signin = (req, res) => {
User.findOne({
username: req.body.username,
})
.populate("roles", "-__v")
.exec((err, user) => {
if (err) {
res.status(500).send({ message: err });
return;
}
if (!user) {
return res.status(404).send({ message: "User Not found." });
}
var 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
});
var authorities = [];
for (let i = 0; i < user.roles.length; i++) {
authorities.push("ROLE_" + user.roles[i].name.toUpperCase());
}
res.status(200).send({
id: user._id,
username: user.username,
email: user.email,
roles: authorities,
accessToken: token,
});
});
};
Route
module.exports = function (app) {
app.use(function (req, res, next) {
res.header(
"Access-Control-Allow-Headers",
"x-access-token, Origin, Content-Type, Accept"
);
next();
});
app.post(
"/api/auth/signup",
[
verifySignUp.checkDuplicateUsernameOrEmail,
verifySignUp.checkRolesExisted,
],
controller.signup
);
app.post("/api/auth/signin", controller.signin);
And my server
const express = require("express");
const bodyParser = require("body-parser");
const cors = require("cors");
const jwt = require("jsonwebtoken");
const mongoose = require("mongoose");
const Quote = require("./models/Quote");
const quoteRoute = require("./routes/quoteRoute");
const quoteController = require("../Maxico/controllers/quoteController");
const config = require("./config/config");
const verifySignup = require("./middlewares/verifySignUp");
const Role = require("./models/Role");
const app = express();
//Import routes
//const authRoute = require("./routes/auth");
var corsOptions = {
origin: "http://localhost:8080/?#/",
};
app.use(cors(corsOptions));
app.use(express.urlencoded({ extended: true }));
app.use(express.json()); //
const db = require("./models/Quote");
mongoose
.connect(
"url",
{
useNewUrlParser: true,
useUnifiedTopology: true,
useFindAndModify: false,
}
)
.then(() => {
console.log("Connected to the database!");
})
.catch((err) => {
console.log("Cannot connect to the database!", err);
process.exit();
});
app.use(express.json());
app.get("/", (req, res) => {
res.send("Welcome to homepage");
});
app.use("/quote", quoteRoute);
require("./routes/authRoute")(app);
//require("./routes/userRoute")(app);
// initial roles
Role.estimatedDocumentCount((err, count) => {
if (!err && count === 0) {
new Role({
name: "user",
}).save((err) => {
if (err) {
console.log("error", err);
}
console.log("added 'user' to roles collection");
});
new Role({
name: "moderator",
}).save((err) => {
if (err) {
console.log("error", err);
}
console.log("added 'moderator' to roles collection");
});
new Role({
name: "admin",
}).save((err) => {
if (err) {
console.log("error", err);
}
console.log("added 'admin' to roles collection");
});
new Role({
name: "superadmin",
}).save((err) => {
if (err) {
console.log("error", err);
}
console.log("added 'superadmin' to roles collection");
});
}
});
// set port, listen for requests
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}.`);
});
In my network tab the request pay load got sent like this:
{payload: {username: "jon", password: "password"}}
payload: {username: "jon", password: "password"}
But my postman only accepts this:
{username: "jon", password: "password"}
So in my action i sent like this:
const actions = {
async signIn(_, payload) {
console.log(payload);
const response = await axios.post(
"http://localhost:3000/api/auth/signin",
payload,
{
headers: {
"Content-Type": "application/json",
},
}
);
console.log(payload);
console.log(response.data);
console.log(response.headers);
console.log(response.status);
},
};
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?
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.