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