PassportJS Multi-tenancy with ExpressJS - node.js

I tried to implement a solution where I have a single ExpressJS app and two Mongoose models for users, one for standard users and the other for administrators. I'm using passport-local-mongoose for the user models. However, I was having an issue where authentication would succeed but the user would not stay in session (req.user would be undefined). I thought that there would be a conflict having two Passport Local strategies with one ExpressJS app. I've been searching for examples and nothing is working. Please see my code below:
index.js
'use strict';
const express = require('express');
const bodyParser = require('body-parser');
const Passport = require('passport').Passport,
appPass = new Passport(),
adminPass = new Passport();
const mongoose = require('mongoose');
const cookieSession = require('cookie-session');
const path = require('path');
const keys = require('./config/keys');
require('./models/User');
require('./models/AdminUser');
require('./models/EM');
mongoose.Promise = global.Promise;
mongoose
.connect(keys.mongoURI)
.then(() => console.log('DB connection successful'))
.catch(err => console.error(err));
// Create new instances of express
const app = express();
const adminApp = express();
// View engine setup
adminApp.set('views', path.join(__dirname, 'views/admin'));
adminApp.set('view engine', 'pug');
// Tell express to use the body-parser middleware and to not parse extended bodies
adminApp.use(bodyParser.urlencoded({ extended: true }));
adminApp.use(bodyParser.json());
const AdminUser = mongoose.model('AdminUser');
adminPass.use(AdminUser.createStrategy());
adminPass.serializeUser(AdminUser.serializeUser());
adminPass.deserializeUser(AdminUser.deserializeUser());
adminApp.use(
cookieSession({
maxAge: 30 * 24 * 60 * 60 * 1000,
keys: [keys.adminCookieKey]
})
);
adminApp.use(adminPass.initialize());
adminApp.use(adminPass.session());
adminApp.use(express.static('public'));
require('./routes/adminRoutes')(adminApp);
adminApp.listen(5001, function(err) {
if (err) {
console.log(err);
}
console.log('Admin server started on port 5001');
});
// app.use(cors());
// View engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');
// Tell express to use the body-parser middleware and to not parse extended bodies
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
const User = mongoose.model('User');
appPass.use(User.createStrategy());
appPass.serializeUser(User.serializeUser());
appPass.deserializeUser(User.deserializeUser());
app.use(
cookieSession({
maxAge: 30 * 24 * 60 * 60 * 1000,
keys: [keys.cookieKey]
})
);
app.use(appPass.initialize());
app.use(appPass.session());
app.use(express.static('public'));
require('./routes/authRoutes')(app);
require('./routes/portalRoutes')(app);
require('./routes/apiRoutes')(app);
if (process.env.NODE_ENV === 'production') {
// Express will serve up production assets
// like our main.js file, or main.css file!
app.use(express.static('client/build'));
// Express will serve up the index.html file
// if it doesn't recognize the route
app.get('*', (req, res) => {
res.sendFile(path.resolve(__dirname, 'client', 'build', 'index.html'));
});
}
const PORT = process.env.PORT || 5000;
app.listen(PORT, function(err) {
if (err) {
console.log(err);
}
console.log('Server started on port 5000');
});
module.exports = {
adminPass: adminPass,
appPass: appPass
};
adminRoutes.js
var Index = require('../index');
var admin = require('../controllers/AdminController.js');
module.exports = app => {
// route to admin dashboard page
app.get('/adminDB', admin.adminDB);
// route to admin login page
app.get('/adminLogin', admin.adminLogin);
// route for admin login action
app.post(
'/adminLogin',
Index.adminPass.authenticate('local'),
admin.adminDoLogin
);
// route for admin logout action
app.get('/adminLogout', admin.adminLogout);
};
When I run this, I get an error that authenticate can't be called on undefined. May be I'm more far off from the proper solution having gone this route but I have no idea how to fix the req.user problem using the previous approach. Does anyone know how to implement multi-tenancy, ideally with only one ExpressJS app?

Related

ReferenceError: done is not defined

const express = require('express');
const cookieParser = require('cookie-parser');
const port = 8000;
const app = express();
const expressLayout = require('express-ejs-layouts');
const db = require('./config/mongoose')
// used for session cookie
const session = require('express-session')
const passport = require('passport')
const passportLocal = require('./config/passport-local-strategy')
app.use(express.urlencoded())
app.use(cookieParser());
// where to look static files like css,js
app.use(express.static('./assets'))
// this line must be above the routes line (line no. 11 in this case) because in the routes all the views are going to be render and before that we have to tell to the browser the layout
app.use(expressLayout)
// extract style and scripts from sub pages into the layout
app.set('layout extractStyles', true);
app.set('layout extractScripts', true);
// set up the view engine
app.set('view engine', 'ejs');
app.set('views', './views');
app.use(session({
name: 'iFacebook',
// TODO change the secret before deployment in production mode
secret: 'Coder',
saveUninitialized: false,
resave: false,
cookie: {
maxAge : (1000*60*100)
}
}))
app.use(passport.initialize());
app.use(passport.session())
// use express router
// require('./routes/index) is similar to require('./routes) in this case, it by default fetch routes
app.use('/', require('./routes/index'))
app.listen(port, (err) => {
if (err) {
console.log(`Error in running the server : ${err}`);
}
console.log(`Server is listening at ${port}`);
})
I am using passport and passport-local strategy and this error comes and even i did not know from which file this error comes. I am sharing the index.js file code which is the server file. This is the first time i am using this even on the documentation i did not found anything

Localhost keeps loading in nodejs

I'm making a authentication system, here is my index.html file i'm using nodejs, express and for database mongodb.
Before it working fine but somehow now its give me no result on visual studio i find no error but the issue is it not giving me the result the keeps loading on the localhost page, i try to change the port but same issue.
const express = require('express');
const cookieParser = require('cookie-parser');
const app = express();
const port = 7000;
// for database
const db = require('./config/mongoose');
const user=require('./models/user');
// for passport authentication
const passport=require('passport');
const passportLocalStrategy=require('./config/passport-local');
const session = require('express-session');
const MongoStore = require('connect-mongo');
app.use(express.urlencoded());
app.use(cookieParser());
// for layouts
const layouts=require('express-ejs-layouts');
app.use(layouts);
// extract style and scripts from sub pages into the layout
app.set('layout extractStyles', true);
app.set('layout extractScripts', true);
//for static files
app.use(express.static('./assets'));
const path=require('path');
// set up the view engine
app.set('view engine', 'ejs');
app.set('views',path.join(__dirname,'views'));
//app.set('views', './views');
app.use(session({
name:'Authentication',
secret:'Dheeraj',
saveUninitialized:false,
resave:false,
cookie:{
maxAge:(1000*60*60)
},
store:( MongoStore.create({
mongoUrl: 'mongodb://localhost/NODEJS_AUTHENTICATION',
autoRemove : 'disabled'
},
function(err){
console.log(err || "connect-mongodb")
}))
}));
app.use(passport.initialize());
app.use(passport.session());
app.use(passport.setUserToLocals);
const passportOauth2Strategy = require('./config/passport-google-oauth2');
const flash=require('connect-flash');
app.use(flash());
app.use(function(req,res,next){
res.locals.flash ={
'success': req.flash('success'),
'error': req.flash('error')
}
next();
});
//use express router
app.use('/',require('./routes'));
app.listen(port, function(err){
if (err){
console.log(`Error in running the server: ${err}`);
}
console.log(`Server is running on port: ${port}`);
});

How to config passport-jwt module? I have an error: Login sessions require session support

Hey there!
I'm starting to learn Node.js courses and in one of it the guy making a registration and authentication with MEAN stack. But his code is old and not working today with updated modules. But I fixed alot of bugs (thx to youtube comments) but when I testing http requests I still have an error:
Error: Login sessions require session support. Did you forget to use `express-session` middleware?
BUT Google says
This module lets you authenticate endpoints using a JSON web token. It is intended to be used to secure RESTful endpoints without sessions.
There is index file:
const express = require('express');
const path = require('path');
const bodyParser = require('body-parser');
const cors = require('cors');
const passport = require('passport');
const mongoose = require('mongoose');
const config = require('./config/database');
// Connect To Database
mongoose.connect(config.database);
// On Connection
mongoose.connection.on('connected', () => {
console.log('Connected to Database '+config.database);
});
// On Error
mongoose.connection.on('error', (err) => {
console.log('Database error '+err);
});
const app = express();
const users = require('./routes/users');
// Port Number
const port = process.env.PORT || 3030;
// CORS Middleware
app.use(cors());
// Set Static Folder
app.use(express.static(path.join(__dirname, 'public')));
// Body Parser Middleware
app.use(bodyParser.json());
// Passport Middleware
app.use(passport.initialize());
app.use(passport.session());
require('./config/passport')(passport);
app.use('/users', users);
// Index Route
app.get('/', (req, res) => {
res.send('invaild endpoint');
});
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname, 'public/index.html'));
});
// Start Server
app.listen(port, () => {
console.log('Server started on port '+port);
});

Express server middleware execute twice?

After trying to log some data on index file. I found my express server execute twice. Why do i get this error/bug?
Running Node 12.13.0 LTS, Express 4.17.1 and latest packages versions by the date of this post. I’ve tried on commenting some parts of code and always seem to end up running twice.
My app.js code:
const express = require('express');
const expressLayouts = require('express-ejs-layouts');
const path = require('path');
const bodyParser = require('body-parser');
const favicon = require('serve-favicon');
const app = express();
// ENV Variables
require('dotenv').config();
const PORT = process.env.PORT;
// Authentication Packages
const session = require('express-session');
const passport = require('passport');
// Middlewares
app.use(favicon(path.join(__dirname,'public','images','favicon.ico')));
app.use(bodyParser.urlencoded({extended: false}));
app.use(express.json());
app.use(session({
secret: 'GBR6N7^?5Xx-Ldqxf&*-Hv$',
resave: false,
saveUninitialized: false,
//cookie: { secure: true }
}));
app.use(passport.initialize());
app.use(passport.session());
// View Engine
app.use(expressLayouts);
app.use(express.static(path.join(__dirname, 'public')));
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
// Routes
app.use('/', require('./routes/index'));
// Controllers
app.use('/profile', require('./routes/profile'));
app.use('/products', require('./routes/products'));
app.use('/bookmarks', require('./routes/bookmarks'));
// Catch 404
app.use((req, res) => {
res.render('pages/404');
});
app.listen(PORT, () => console.log(`Server started on port ${PORT}`));
and my index.js code:
const express = require('express');
const router = express.Router();
// Official pages
router.get('/', (req, res) => {
// THIS IS THE CODE I GET TWICE ON CONSOLE
console.log(req.user);
console.log(req.isAuthenticated());
// THIS IS THE CODE I GET TWICE ON CONSOLE
res.render('pages/index');
});
router.get('/about', (req, res) => {
res.render('pages/about');
});
router.get('/features', (req, res) => {
res.render('pages/features');
});
// Footer pages
router.get('/terms', (req, res) => {
res.render('pages/terms');
});
router.get('/refunds', (req, res) => {
res.render('pages/refunds');
});
module.exports = router;
Also i have those two functions on my profile.js (for passport.js):
passport.serializeUser((userId, done) => {
done(null, userId);
});
passport.deserializeUser((userId, done) => {
done(null, userId);
});
I get those results twice:
console.log(req.user);
console.log(req.isAuthenticated());
Output (Executed twice!):
undefined
false
undefined
false
and I expect one:
undefined
false
Due to how Express routing works, the path / will match / and /about and /favicon.ico etc. This is because Express supports not just endpoint routing but also path mounting. In other words, express supports things like this:
const app = express();
const route = express.Router();
route.get('/world', (req, res) => { res.send('hello') });
app.get('/hello', route); // mounts world to hello
// so we can access /hello/world
In order to support the feature above, express needs to interpret paths such as /hello to mean both /hello and /hello/anything/else. It needs to treat it as both the endpoint and potentially just a path leading to an endpoint.
This means that if you have a path:
app.get('/', () => {});
It will also trigger if the browser requests /favicon.ico. And browsers request favicon.ico to draw the tiny icon in the browser tab. This is why your route is triggered twice.
A few things to keep in mind when writing Express routes/controllers:
Make sure that the / path is last because otherwise it will also respond to requests to all your other paths.
If you are using express.static() make sure it is set up before the / path. Yes, the first rule above should also cover this but I see this issue often enough that it merits its own point.
In your case you can possibly fix the issue by simply creating a favicon.ico icon and saving it in the static (public) folder.

Socket.io connecting many times instead of just once

I have build this app, with express.js, its a basic webapp, but now i want to add a simple messaging system.
I already had built my express app and server like this:
const app = require('./app');
app.set('port', process.env.PORT || 7777);
const server = app.listen(app.get('port'), () => {
console.log(`Express running → PORT ${server.address().port}`);
But as I follow this tutorial on socket.io: https://socket.io/get-started/chat/
I changed my start.js to this, to use socket.io:
const app = require('./app');
app.set('port', process.env.PORT || 7777);
const server = app.listen(app.get('port'), () => {
console.log(`Express running → PORT ${server.address().port}`);
});
const io = require('socket.io').listen(server);
io.on('connection', function(socket){
console.log('a user connected');
});
It says on the tutorial that when a user signs up i should see a console log saying: a user is connected
However I get one log per frame... not just one per connected user.
Is this behaviour correct with the changes i made.. or should i still be getting still just one log per connected user?
Im also using pug as my templating language, and i have, at the end of my layout file this:
block scripts
script(src=`https://maps.googleapis.com/maps/api/js?key=${process.env.MAP_KEY}&libraries=places`)
script(src="/dist/App.bundle.js")
script(src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.2.0/socket.io.js")
script.
const socket = io()
App.js:
const express = require('express');
const session = require('express-session');
const mongoose = require('mongoose');
const MongoStore = require('connect-mongo')(session);
const path = require('path');
const cookieParser = require('cookie-parser');
const bodyParser = require('body-parser');
const passport = require('passport');
const promisify = require('es6-promisify');
const flash = require('connect-flash');
const expressValidator = require('express-validator');
const routes = require('./routes/index');
const helpers = require('./helpers');
const errorHandlers = require('./handlers/errorHandlers');
require('./handlers/passport');
// create our Express app
const app = express();
// view engine setup
app.set('views', path.join(__dirname, 'views')); // this is the folder where we keep our pug files
app.set('view engine', 'pug'); // we use the engine pug, mustache or EJS work great too
// serves up static files from the public folder. Anything in public/ will just be served up as the file it is
app.use(express.static(path.join(__dirname, 'public')));
// Takes the raw requests and turns them into usable properties on req.body
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
// Exposes a bunch of methods for validating data. Used heavily on userController.validateRegister
app.use(expressValidator());
// populates req.cookies with any cookies that came along with the request
app.use(cookieParser());
// Sessions allow us to store data on visitors from request to request
// This keeps users logged in and allows us to send flash messages
app.use(session({
secret: process.env.SECRET,
key: process.env.KEY,
resave: false,
saveUninitialized: false,
store: new MongoStore({ mongooseConnection: mongoose.connection })
}));
// // Passport JS is what we use to handle our logins
app.use(passport.initialize());
app.use(passport.session());
// // The flash middleware let's us use req.flash('error', 'Shit!'), which will then pass that message to the next page the user requests
app.use(flash());
// pass variables to our templates + all requests
app.use((req, res, next) => {
res.locals.h = helpers;
res.locals.flashes = req.flash();
res.locals.user = req.user || null;
res.locals.currentPath = req.path;
next();
});
// promisify some callback based APIs
app.use((req, res, next) => {
req.login = promisify(req.login, req);
next();
});
// After allllll that above middleware, we finally handle our own routes!
app.use('/', routes);
// If that above routes didnt work, we 404 them and forward to error handler
app.use(errorHandlers.notFound);
// One of our error handlers will see if these errors are just validation errors
app.use(errorHandlers.flashValidationErrors);
// Otherwise this was a really bad error we didn't expect! Shoot eh
if (app.get('env') === 'development') {
/* Development Error Handler - Prints stack trace */
app.use(errorHandlers.developmentErrors);
}
// production error handler
app.use(errorHandlers.productionErrors);
// done! we export it so we can start the site in start.js
module.exports = app;
The problem was that i had opened multiple tabs with the app, therefore all the logs where from other tabs being connected.

Resources