Struggle with passport and jwt — by Steven Lacerda (Morgan Hill, CA)
3 min readOct 21, 2018
I was having a heck of a time with passport and jwt. So, here’s what I realized that I needed to do in order to get req.user in my routes. Of course, I knew that I needed a middleware, but what???
// middleware to test user authentication and set in req.user
function verifyUser(req, res, next) {
passport.authenticate('jwt', { session: false }, (err, user, info) => {
if (err || !user) {
return res.status(400).json({
message: info.message
});
}
req.user = user;
next()
})(req, res, next);
}
And then in my routes:
app.use("/api/invoices", verifyUser, invoices);
Just to cover all the bases here, my passport config file looks like:
const JwtStrategy = require('passport-jwt').Strategy;
const ExtractJwt = require('passport-jwt').ExtractJwt;
const mongoose = require('mongoose');
const User = require('../models/User');
const keys = require('../config/keys');const options = {};
options.jwtFromRequest = ExtractJwt.fromAuthHeaderAsBearerToken();
options.secretOrKey = keys.secretOrKey;module.exports = passport => {
passport.use(new JwtStrategy(options, (payload, done) => {
User.findById(payload.id)
.then(user => {
if (user) {
return done(null, user);
} else {
return done(null, false);
}
})
.catch(err => console.log(err))
}));
};
and my server.js file:
const mongoose = require('mongoose');
const express = require("express");
const app = express();
const path = require('path');
const morgan = require('morgan');const db = require('./config/keys').mongoURI;
mongoose
.connect(db, { useNewUrlParser: true })
.then(() => console.log("Connected to MongoDB successfully"))
.catch(err => console.log(err));const bodyParser = require('body-parser');
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());// setup logger
app.use(morgan('dev'));const passport = require('passport');
app.use(passport.initialize());
require('./config/passport')(passport);// setup static folder for frontend
app.use(express.static( `${__dirname}/frontend/build`));// router routes
const users = require("./routes/api/users");
const invoices = require("./routes/api/invoices");
const clients = require("./routes/api/clients");
const subs = require("./routes/api/subscriptions");// middleware to test user authentication and attach user as req.user
function verifyUser(req, res, next) {
passport.authenticate('jwt', { session: false }, (err, user, info) => {
if (err || !user) {
return res.status(400).json({
message: info.message
});
}
req.user = user;
next()
})(req, res, next);
}app.get("/", (req, res) => res.send(""));
app.use("/api/users", users);
app.use("/api/invoices", verifyUser, invoices);
app.use("/api/clients", verifyUser, clients);
app.use("/api/subscriptions", verifyUser, subs);// serve up index.html for frontend
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname, '/frontend/build/index.html'));
});const port = process.env.PORT || 5000;app.listen(port, () => console.log(`Server is running on port ${port}`));
And my login and register routes:
const express = require("express");
const router = express.Router();
const bcrypt = require('bcryptjs');
const User = require('../../models/User');
const jwt = require('jsonwebtoken');
const keys = require('../../config/keys');
const passport = require('passport');
const validateRegisterInput = require('../../validation/register');
const validateLoginInput = require('../../validation/login');router.post('/register', (req, res) => {
const { errors, isValid } = validateRegisterInput(req.body);if (!isValid) {
return res.status(400).json(errors);
}User.findOne({ email: req.body.email })
.then(user => {
if (user) {
// errors.name = "User already exists";
return res.status(400).json({ email: "A user has already registered with this address"})
} else {
const newUser = new User(req.body);
// don't store plaintext password
bcrypt.genSalt(10, (err, salt) => {
bcrypt.hash(newUser.password, salt, (err, hash) => {
if (err) throw err;
newUser.password = hash;
newUser
.save()
.then(user => {
const payload = { id: user.id, name: user.name };jwt.sign(payload, keys.secretOrKey, { expiresIn: 86400 * 7 }, (err, token) => {
res.json({
success: true,
token: "Bearer " + token
});
});
})
.catch(err => console.log(err));
});
});
}
});
})router.post('/login', (req, res) => {
const { errors, isValid } = validateLoginInput(req.body);if (!isValid) {
return res.status(400).json(errors);
}const email = req.body.email;
const password = req.body.password;User.findOne({email})
.then(user => {
if (!user) {
errors.name = "This user does not exist";
return res.status(404).json({email: 'This user does not exist'})
}bcrypt.compare(password, user.password)
.then(isMatch => {
if (isMatch) {
const payload = { id: user.id, name: user.name };jwt.sign(payload, keys.secretOrKey, {expiresIn: 3600 }, (err, token) => {
res.json({
success: true,
token: 'Bearer ' + token
});
});
} else {
errors.password = "Incorrect password";
return res.status(400).json({password: 'Incorrect password'});
}
})
})
})
Solving a problem is as good as those sexual feelings…sometimes, okay, maybe not. Straight from Morgan Hill, CA. Good luck!
by Steven Lacerda