So I am trying to build two JWT authentication systems for one app. One for the users and one for businesses. The authentication uses passport (local and JWT). I started working on the user aspect of the authentication and I was able to make it work as I wanted. I thought it would be the same for business. All I did was simply copy paste and change few variable. Normally the business endpoints should work but it does not. So I tried to debug what was going on and I am still trying to figure out what it is. My guess is that when I load the strategies in my app.js, only the passport strategies for users get called. If I permute positions between passport and business-passport, everything breaks.
Here is my code.
Passport Middleware For User passport.js
const passport = require('passport');
const JwtStrategy = require('passport-jwt').Strategy;
const {ExtractJwt} = require('passport-jwt');
const LocalStrategy = require('passport-local').Strategy;
const { JWT_SECRET } = require('./config');
const User = require('./models/user');
// const Business = require('./models/business');
passport.use(new JwtStrategy({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
secretOrKey: JWT_SECRET,
passReqToCallback: true
}, async (req, payload, done) => {
console.log('payload', payload)
try {
// Find the user specified in token
const user = await User.findById(payload.sub);
// Handle if User dont exist
if (!user) {
return done(null, false);
}
// Otherwise, return the user
req.user = user;
done(null, user);
} catch (error) {
done(error, false);
}
}));
// Local Strategy
passport.use(new LocalStrategy({
usernameField: 'email'
}, async (email, password, done) => {
try {
// Find the user given the email
const user = await User.findOne({email});
// If not, handle it
if (!user) {
return done(null, false);
}
// check if password is correct
const isMatch = await user.isValidPassword(password);
// if not, handle it
if (!isMatch) {
return done(null, false);
}
// Otherwise, return the user
done(null, user);
} catch (error) {
done(error, false);
}
}));
Passport Middleware For Business passport-business.js
Just change User for Business
Controllers for Users controllers/user.js
const fs = require('fs');
const mongoose = require('mongoose');
const JWT = require('jsonwebtoken');
const { JWT_SECRET } = require('../config');
const User = require('../models/user');
signToken = user => {
// Respond with token
return JWT.sign({
iss: 'My Business, Inc.',
userType: 'users',
sub: user._id, /* You can also use newUser.id */
iat: new Date().getTime(), /* Current Time */
exp: new Date().setDate(new Date().getDate() + 1), /* Current Time + 1 day ahead */
}, JWT_SECRET);
};
module.exports = {
signup: async (req, res, next) => {
console.log('req.value.body', req.value.body);
const {email, password} = req.value.body;
// Check if User already exist by email address
const foundUser = await User.findOne({email});
if(foundUser) {
return res.status(403).json({error: 'Email is already in use'});
}
// Create a new User
const newUser = new User({email, password});
await newUser.save();
// Generate the token
const token = signToken(newUser);
// Respond with token
res.status(200).json({token});
},
signin: async (req, res, next) => {
// Generate token
const token = signToken(req.user);
res.status(200).json({token});
// console.log('signin');
},
};
The reason why I have req.value... is because of a middleware that I used to validate my body and url params.
Controllers for Business controllers/business.js . Everything is the same, just change User with Business.
I should also point out that whenever I use jwt.io to see if my token signature is valid, it always says invalid signature, but everything works on my application as expected. Wondering why it is saying invalid signature.
Routes for Users routes/user.js . Please remember that it is the same for Business.
const express = require('express');
// this router deals better with "try{} catch{}" situations"
const router = require('express-promise-router')();
const passport = require('passport');
const UserController = require('../controllers/user');
require('../passport');
const { validateBody, schemas, validateParam } = require('../helpers/route-helpers');
const StrategyLocal = passport.authenticate('local', {session: false});
const StrategyJWT = passport.authenticate('jwt', {session: false});
router.get('/', (req, res) => {
res.json({message: "Welcome to our User's API Endpoint!"});
});
router.route('/signup')
// .get(UserController.index)
.post([validateBody(schemas.authSchema)], UserController.signup);
router.route('/signin')
// Makes sense to implement localstrat here because jWt will always work because the user was issued a token at signup
// So both solutions will work here as a strategy
.post(validateBody(schemas.authSchema), StrategyLocal, UserController.signin);
module.exports = router;
And finally, the file that I think is causing the problem... Introducing...
app.js
const express = require('express');
const logger = require('morgan');
const mongoose = require('mongoose');
const bodyParser = require('body-parser');
const passport = require('passport');
const path = require('path');
const cors = require('cors');
const { dbName } = require('./config');
mongoose.Promise = global.Promise;
mongoose.connect('mongodb://localhost/' + dbName, {
useNewUrlParser:true,
useUnifiedTopology:true,
useCreateIndex: true,
useFindAndModify: false
});
const app = express();
// 👇👇👇👇 This is where the problem is happening in my opinion
require('./passport-business')
require('./passport')
const user = require('./routes/user');
const business = require('./routes/business');
// Middlewares
// set the static files location /public/img will be /img for users
app.use(logger('dev'));
app.use(cors());
app.use(bodyParser.urlencoded({ extended: false}));
app.use(bodyParser.json());
app.use((req, res, next) => {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization, noauth");
if (req.method === 'OPTIONS'){
res.header("Access-Control-Allow-Methods", "PUT, POST, PATCH, DELETE, OPTIONS");
res.setHeader('Access-Control-Allow-Credentials', false);
return res.status(200).json({});
}
next();
});
app.use('/api/users', user);
app.use('/api/businesses', business);
// Start the server
const port = app.get('port') || 2019;
app.listen(port, () => console.log(`Server is listening on port ${port}`));
If I keep everything as is, all User endpoints work, signup works for Business, but I get 401 when I try to signin (Business). But if I do this...
require('./passport')
require('./passport-business')
I get 401 when I try to signin (User) and I get 500 when I try to signin (Business).
Can someone tell me what I am doing wrong here? Maybe it is the order in which I load these files in app.js. Maybe it is the fact that I am creating two separate passport files for User and Business. I would love to know how to combine those two into one. .
from Node.js - JWT, Passport.js, API, Authentication: How To Implement Multiple Passport.js Middleware?
No comments:
Post a Comment