Execute a function before rendering express get router NodeJS - node.js

I want to execute a function each time when the express router is called.
I know I could have placed the function simply inside the app.get function, but I want to call the same function multiple times.
Here is my router.js file:
var Setting = require('../models/setting');
module.exports = function(app, passport) {
// =====================================
// HOME PAGE (with login links) ========
// =====================================
app.get('/', function(req, res) {
Setting.findOne(function(err, setting) {
if (err)
throw err;
// console.log(setting);
res.render('index', { title: 'eduBird | Reach the glory', setting: setting }); // load the index file
});
});
// =====================================
// LOGIN ===============================
// =====================================
// show the login form
app.get('/login', sabSettings, function(req, res) {
// render the page and pass in any flash data if it exists
res.render('login', {
message: req.flash('loginMessage'),
errors: req.flash('error'),
title: 'Login | eduBird',
setting: setting
});
});
// process the login form
app.post('/login', passport.authenticate('local-login', {
successRedirect: '/profile',
failureRedirect: '/login',
failureFlash: true
}));
// =====================================
// SIGNUP ==============================
// =====================================
// show the signup form
app.get('/signup', function(req, res) {
// render the page and pass in any flash data if it exists
res.render('signup', {
message: req.flash('signupMessage'),
errors: req.flash('error'),
title: 'Register | eduBird',
setting: req.setting
});
});
// process the signup form
app.post('/signup', passport.authenticate('local-signup', {
successRedirect: '/profile',
failureRedirect: '/signup',
failureFlash: true
}));
// app.post('/signup', function(req, res) {
// console.log(req);
// });
// =====================================
// PROFILE SECTION =====================
// =====================================
// we will want this protected so you have to be logged in to visit
// we will use route middleware to verify this (the isLoggedIn function)
app.get('/profile', isLoggedIn, sabSettings, function(req, res) {
res.render('profile', {
user: req.user, // get the user out of session and pass to template
title: req.user.local.name + "'s profile | eduBird",
setting: req.setting
});
});
// =====================================
// LOGOUT ==============================
// =====================================
app.get('/logout', function(req, res) {
req.logout();
res.redirect('/');
});
};
// route middleware to make sure a user is logged in
function isLoggedIn(req, res, next) {
// if user is authenticated in the session, carry on
if (req.isAuthenticated())
return next();
// if they aren't redirect them to the home page
res.redirect('/login');
};
function sabSettings(next) {
Setting.findOne(function(err, setting) {
if (err)
throw err;
console.log('sabSetting function executed');
console.log(setting);
console.log('~~~~~~~~~~~');
// return setting;
return next(setting);
});
};
Here I had used an example of isLoggedIn which is executing fine, but the same is not able to work for sabSettings() which would pass all settings config from database to, my /login, /signup, /profile and/or / all the routes.
The console.log(setting) is returning all the data to my console, but I am getting an error stating:
throw er; // Unhandled 'error' event
^
TypeError: next is not a function
at C:\Users\animeshweb\Desktop\projects\eb-v2\routes\routes.js:106:16
You can see that I had embedded a function in app.get('/') for getting the same, but I want this function to be executed wherever I want, so I want a separate function for that.
Update
I update my routes.js as requested:
function sabSettings(req, res, next) {
Setting.findOne(function(err, setting) {
if (err)
next(err);
console.log('sabSetting function executed');
console.log(setting);
console.log('~~~~~~~~~~~');
req.setting = setting;
next();
});
};
I had also made Setting = require(myModelURL) above, which is working fine fo route.get'/'.
*This is my view/layout.pug file`
link(rel='icon', type='image/png', href=setting.logo.logo16 sizes='16x16')
link(rel='icon', type='image/png', href=setting.logo.logo32 sizes='32x32')
link(rel='icon', type='image/png', href=setting.logo.logo128 sizes='128x128')
The same is working fine in adminlayout0.pug
Thanks.

You've declared sabSettings wrong. Middleware is passed three arguments so your declaration should be like this:
function sabSetting(req, res, next) {
// function logic here
}
Since the argument you named next was in the wrong position, it was not a function when you tried to call it.
And, I don't know why you are trying to do return next(setting). That will tell Express that you're reporting an error in the request. If you're trying to put the setting value somewhere that the rest of the request handlers can use it, then you probably want to put it on the req object such as:
req.setting = setting; // put setting on the req object for other code to use
next(); // continue routing
In addition, you should never do a throw err inside a middleware function. That simply won't do anything useful and your request will probably never get finished. Instead, you should handle the error when you get it. You may either branch to some alternate strategy within the middleware when you get an error or you may simply returned a failed request with either next(err) or by doing res.status(500).send(...).
You will then need to change your res.render() to this:
res.render('index', { title: 'eduBird | Reach the glory', setting: req.setting });
The setting variable is now stored on the req object so that's where you need to refer to it.
Change it everywhere you are referring to setting.

Related

Why does (passport) middleware function fire with app.get, but not app.post

I have the following middleware function:
function isLoggedIn(req, res, next) {
if (req.isAuthenticated()) {
console.log('***User is logged in***');
next();
} else {
res.redirect('/');
console.log('***User IS NOT logged in***');
}
}
This works just fine in the following GET route:
app.get('/profile', isLoggedIn, function(req, res) {
res.render('profile.ejs', {
user: req.user // get the user out of session and pass to template
});
});
However, when I try to plug in the isLoggedIn function into a POST route, it doesn't fire:
app.post('/add', isLoggedIn, function(req, res) {
console.log('Success');
});
Why doesn't my function fire with app.post, but with app.get, it fires?
If you're posting to the url with a promise library like jQuery or Axios, then res.redirect will not change the page the browser is on. In this case, you would need to handle the response of your post resulting in a status code of 302 (default status code return on Expresses redirect) then have that redirect the page to where you need (on the front end)

NodeJS, passport-jwt: Authenticate all user except in list

I am setting up a nodejs project with passportjs and passport-jwt. I see where you can specify passport.authenticate for each route you want to secure. However, I do not see a way to lock down all router except maybe login and register. I see where express-jwt allows for the use of express-unless, which seems to accomplish this functionality. Is there a similar mechanism for passport-jwt and if so how would this be accomplished?
Actually you don't even need express-unless you can use the fact that express allow to register middlewares that get executed all the time to do your filtering
const express = require('express');
const app = express();
function authenticateSomeRoutesMiddleware(req, res, next) {
if (/(login|register)/.test(req.originalUrl)) {
// No authentication needed
return next();
} else {
// Option 1 => use default passport logic
// which respond with a 401 unauthorized status if authentication fails
passport.authenticate('jwt', { session: false}), function(req, res, next) {
// Do something now you know that the user has been authenticated
return next(); // this will call the next middleware on the stack
})(req, res, next);
// Option 2: use a custom callback to allow your application
// to handle success or failure
// As per passport spec:
// - If authentication failed, user will be set to false.
// - If an exception occurred, err will be set.
// - An optional info argument will be passed, containing additional details
// provided by the strategy's verify callback.
passport.authenticate('local', function(err, user, info) {
if (err) {
// Error in authentication process; handle it or call...
return next(err);
}
if (!user) {
// Authentication failed (based on your strategy's implementation)
// You can for example try again
return res.redirect('/login');
}
// If you are using session to store the user call req.logIn() else call `return next()` directly
req.logIn(user, function(err) {
if (err) { return next(err); }
return next();
});
})(req, res, next);
}
}
// add this BEFORE your route definitions
app.use(authenticateSomeRoutesMiddleware);
// add all your routes here
app.use('/login', function(req, res, next) {
// do something
});
app.use('/register', function(req, res, next) {
// do something else
});
app.use('/some/protected/route', function(req, res, next) {
// this will get called once the authentication process has been cleared
});
//...

Automatically redirect logged in users nodejs passport.js

Currently using node.js, express & passport.js to create a custom website/application.
Having followed several guides, I have a functioning login/logout system with authentication. However, should a user revisit and their session is still active, it doesn't redirect them to the 'dashboard'.
Current root route:
/* GET login page. */
router.get('/',function(req, res) {
// Display the Login page with any flash message, if any
res.render('index', { message: req.flash('message') });
});
I am making use of the isAuthenticated function, as below:
var isAuthenticated = function (req, res, next) {
if (req.isAuthenticated())
return next();
res.redirect('/');
}
How do I get it to automatically redirect users with an existing session? Any pointers most welcome!
Ok, I figured it out. In the / route, I queried whether req.user was set.
/* GET login page. */
router.get('/',function(req, res) {
if(req.user){
res.redirect("/dashboard");
}else{
// Display the Login page with any flash message, if any
res.render('index', { message: req.flash('message') });
}
});
You can attach a middleware with "/" endpoint something like this.
router.get('/', sessionValidate, function(req, res, next) {
res.render('login');
});
Where sessionValidate looks something like this :
function sessionValidate(req,res,next){
console.log(req.user,"i am here");
users.findById(req.user,function(err, user) {
if(user!=null){
req.session.user = user;
res.locals.user=user;
res.redirect("/home")
}
else {
next();
}
});
}

render() - how it loads controllers?

I give 401 Uanthourised acces header with
module.exports = function() {
return function(req, res, next) {
if (!req.isAuthenticated()) {
res.status(401);
console.log('Unauthorized');
res.render('login.html', { csrfToken: req.csrfToken() });
return;
}
next();
};
};
Now login.html is a view handled by completly different controller:
var passport = require('passport');
var config = require(__dirname + '/../lib/config');
module.exports = function (router) {
router.get('/', function (req, res) {
// Error flash messages
var errorFlash = req.flash('error');
if (errorFlash && errorFlash.length) {
res.locals.error = errorFlash;
}
res.render('login.html', { csrfToken: req.csrfToken() });
});
router.post('/',
passport.authenticate('local', {
failureRedirect: '/login/',
failureFlash: true
}),
function(req, res) {
res.cookie(config.cookie.name, req.user.email, { signed: true, maxAge: 1234 });
res.redirect('/');
}
);
};
My concern is how I can be sure what part of login controller fires up? render('index.html') somehow hooks up to router.post() in index controller and login form is handler as usual. How that can be?
If you ever have any doubt which function is being called then a good approach is to (temporarily) add a console.log() within that function. I will often do something like:
console.log('filename.js - functionname() called with args: [%s] [%s]', arg1, arg2);
And then watch the log for information about what fired off and what variables you're trying to watch.
It could be that you need to redirect rather than render in your 401-related code.
res.redirect('/login');
(Edited)
The controller will always go to "index" if you did not provide any method.
The reason that it is a "POST" is probably that you are trying to send a form to the index page.
If you just request the index page. for example "(url)/login/index" or just "(url)/login" then it would be a "GET" request, but what are you trying to tell, I don't understand your question completely

how to send json as a response after passport authenticationin node.js

I am trying this git example.
Which works well when I integrated it with my project, but what I want to achieve is to send json as a response to the client/request, instead of successRedirect : '/profile' & failureRedirect : '/signup'.
Is it possible to send a json, or is there some other methods to get the same?
Any help will be appreciated,TU
here I modified my code to send json as a response
// process the signup form
app.post('/signup', passport.authenticate('local-signup', {
successRedirect : '/successjson', // redirect to the secure profile section
failureRedirect : '/failurejson', // redirect back to the signup page if there is an error
failureFlash : true // allow flash messages
}));
app.get('/successjson', function(req, res) {
res.sendfile('public/index.htm');
});
app.get('/failurejson', function(req, res) {
res.json({ message: 'hello' });
});
You can use passport's authenticate function as route middleware in your express application.
app.post('/login',
passport.authenticate('local'),
function(req, res) {
// If this function gets called, authentication was successful.
// `req.user` contains the authenticated user.
// Then you can send your json as response.
res.json({message:"Success", username: req.user.username});
});
By default, if authentication fails, Passport will respond with a 401 Unauthorized status, and any additional route handlers will not be invoked. If authentication succeeds, the next handler will be invoked and the req.user property will be set to the authenticated user.
Create new route, e.g.: /jsonSend with res.json in it and make successRedirect: '/jsonSend'. That should do it.
There is an official Custom Callback documentation:
app.get('/login', function(req, res, next) {
passport.authenticate('local', function(err, user, info) {
if (err) { return next(err); }
if (!user) { return res.redirect('/login'); }
req.logIn(user, function(err) {
if (err) { return next(err); }
return res.redirect('/users/' + user.username);
});
})(req, res, next);
});
https://github.com/passport/www.passportjs.org/blob/master/views/docs/authenticate.md
Use passport as a middleware.
router.get('/auth/callback', passport.authenticate('facebook'),
function(req, res){
if (req.user) { res.send(req.user); }
else { res.send(401); }
});
// process the signup form
app.post('/signup', passport.authenticate('local-signup', {
successRedirect : '/successjson', // redirect to the secure profile section
failureRedirect : '/failurejson', // redirect back to the signup page if there is an error
failureFlash : true // allow flash messages
}));
app.get('/successjson', function(req, res) {
res.sendfile('public/index.htm');
});
app.get('/failurejson', function(req, res) {
res.json({ message: 'hello' });
});

Resources