Function between route and route handler is not executing as expected - javascript

I am trying to write API's of which some of them has role based authorization. Further I am using JWT for authentication for my API's.
Below is the code, I am trying to write. However, when I hit "/user/:email" endpoint, authorize function is not getting called. Always, getUser function is getting called. I was in an assumption that at first authorize function should be called followed by getUser.
Can someone enlighten me why authorize function is not getting called when I hit "/user/:email" endpoint? What wrong am I doing here? And how can I correctly handle this scenario?
var express = require('express')
var router = express.Router();
var expressJwt = require('express-jwt');
var authorize = function(role){
expressJwt({secret:'secretKey'}, (req, res, next) =>{
console.log('req.role is', req.role);
if(req.role === role){
next();
}else{
res.status(403).send({message: 'Unauthorized'});
}
})
}
var getUser = function(req,res,next){
res.status(200).send("Hello user");
}
router.get('/user/:email', authorize('Admin'), getUser);

authorize is called when the application starts up, not when you hit "/user/:email". Calling authorize('Admin') just returns undefined which will not cause the console.log being executed, neither when "/user/:email" is hit.
I think what you want to achieve is applying multiple callbacks sequentially to one route.
See here
So, authorize('Admin') should return a middleware (also called callback), such as:
return expressJwt({secret:'secretKey'}, (req, res, next) =>{...})

Related

Is it ok for an Express.js middleware to rely on a previous middleware in the chain?

Let's say I have a POST request in my API POST /api/comments where I create a comment. Comments are received in array tree structure (each comment has a children property, which can have subcomments). I have multiple "events" that occur before response is sent.
I can put all the required code into one middleware function like this.
function postComments(req, res, next) {
// handle general authentication
if (!isAuthenticated()) {
next("not authenticated ...");
}
// handle more specific authentication (ex. admin)
if (!isAdmin()) {
next("not admin ...")
}
// validate req.body.comments so that each comment has allowed form
if (!areCommentsValid()) {
next("not valid ...");
}
// modify comments for database insertion
const modifiedComments = modifyComments();
// database insertion
db.insert(modifiedComments)
res.sendStatus(201);
}
In above example general auth and admin authentication can be used in multiple routes and next middlewares are not relying on them, code is still working. So code those 2 makes sense.
In this example my middleware does multiple things.
What I thought I could do was split the following code into multiple middleware.
function userAuth(req, res, next) {
// handle user authentication
}
function adminAuth(req, res, next) {
// handle admin auth
}
function validateComments(req, res, next) {
// handle req.body.comments validation
}
function modifyComments(req, res, next) {
// modify comments
// req.commentsForDb = modifiedComments;
}
function postComments(req, res, next) {
// insert req.commentsForDb into database
}
So now I split my middleware into 4 different middlewares, but the problem is middlewares depend on each other.
postComments requires modifyComments to set req.commentsForDb, modifyComments requires validateComments etc.
Which is the preferred method?
It's perfectly valid and it is actually the way middleware are intended to be used.
As long as you properly call next with an error code when something goes wrong in a middleware where you should stop forwarding to the next one.
The added value here is that you can reuse your middlewares in many different routes. Another thing you can do, is a middleware closure generator, for example an auth middleware based on the role:
function auth(role) {
return function(req, res, next) {
// get my user
// ...
// check user role
if user.role != role {
return next(new Error("Auth failed"))
}
return next()
}
}
// this route is for admins
app.get(
"/foo",
auth("admin"),
foo
)
// this one for users
app.get(
"/bar",
auth("user"),
foo
)

Error when calling the next() method in passport.authenticate('local')

I have some middleware which uses passport.js, which aims to authenticate a user, then move onto the next piece of middleware:
exports.authenticate = (req, res, next) => {
passport.authenticate('local', (err, user, info) => {
console.log('You are authenticated!!')
next()
})(req, res, next)
}
When the user registers, I see You are authenticated!! in my console. So by this logic, the user should be attached to the req. So I call next and it moves onto this piece of middleware (I want to do something else before the user is redirected):
exports.createMatch = async (req, res) => {
console.log(req.user._id)
}
However, an error on my console and webpage shows TypeError: Cannot read property '_id' of undefined. Why is this and how do I rectify it?
routes.js:
router.post(
'/register',
userController.validateRegistration, // validate them
userController.register, // register them to the db
authController.authenticate, // authenticate them
catchErrors(dataController.createMatch) // do some other bits then redirect
)
Fairly new to Express. If more code is needed let me know. Apologies if something similar was answered elsewhere.
Regards,
James.
This is the line in the source where req.user gets set:
https://github.com/jaredhanson/passport/blob/821a474342b1ae900849911b5c3d3ccc4ef5ab86/lib/http/request.js#L44
It's in the method req.login. The documentation is here:
http://www.passportjs.org/docs/login
It states:
When the login operation completes, user will be assigned to req.user.
Further it says:
passport.authenticate() middleware invokes req.login() automatically.
So far everything sounds like it should work...
However, if you read the section about providing a Custom Callback, which is what you're doing, it states:
Note that when using a custom callback, it becomes the application's responsibility to establish a session (by calling req.login()) and send a response.
There are several ways to fix it. You could get rid of the custom callback, you could call login inside the callback, or you could just set req.user = user yourself.

Exception CSRF check for routers node js

I am using webook to call one of my routers in my node js application.
I want to not use 'csurf' which is a library for CSRF token when that specific router is called.
The router is called ch and it is not the index router
My code for the CSRF expection is like this
var csrf = require('csurf');
var csrfProtection = csrf();
var csrfExclusion = ['/check-ch'];
var conditionalCSRF = function (req, res, next) {
if(csrfExclusion.indexOf(req.path) !== -1){
next();
} else {
csrf(req, res, next);
next();
}
}
router.use(conditionalCSRF);
router.post('/check-ch',(req, res, next) => {
console.log(req.body);
console.log('i am here');
});
Normally I searched stackoverflow but all answer shows that the method above should work but in my terminal I end up getting invalid csrf token, and I am not sure why.
The CSRF token is also being used in the index.js router, by the same method, where I load it first and then directly use router.use(csrfProtection), no conditionalCSRF is being used.
It looks like the conditionalCSRF is not even getting called, when I try to use a webook, so I am not sure what is stopping it to run.

What javascript library sets the _parsedUrl property on a request object

I am working with node/express/passport/ looking at code that attempts to use a request like:
req._parsedUrl.pathname;
I cannot figure out where this variable is coming from. Is this a canonical variable name that is set in a common .js library? It doesn't seem exposed in any headers.
req._parsedUrl is created by the parseurl library which is used by Express' Router when handling an incoming request.
The Router doesn't actually intend to create req._parsedUrl. Instead parseurl creates the variable as a form of optimization through caching.
If you want to use req._parsedUrl.pathname do the following instead in order to ensure that your server doesn't crash if req._parsedUrl is missing:
var parseUrl = require('parseurl');
function yourMiddleware(req, res, next) {
var pathname = parseUrl(req).pathname;
// Do your thing with pathname
}
parseurl will return req._parsedUrl if it already exists or if not it does the parsing for the first time. Now you get the pathname in a save way while still not parsing the url more than once.
You can write a middleware to handle then set properties for req.
var myMiddleWare = function () {
return function (req, res, next) {
req._parsedUrl = 'SOME_THING';
next()
}
};
app.get('/', myMiddleWare, function (req, res) {
console.log(req._parsedUrl); // SOME_THING
res.end();
})
Express middleware document in here

Node.js detect if a variable exists in req for every page a user goes to

More specifically, I have an auth system that uses passportjs and req.user is defined if the user is authenticated.
Right now my website only has about 5 pages, but it's growing, and at the top of every route, I check if req.user exists and I pass a true or false variable to the rendered template, and the template renders accordingly.
I messed around with things such as app.get("*") but I didn't end up finding anything good.
How could I check if req.user (or anything else that could exist within req...) exists -- when a user goes to any page of my website, without repeting code?
Progress:
Using this code in app.js:
app.use(function (req, res, next) {
// Using req.locals.isAuthenticated would be better, as it's automatically passed to every rendered templates.
req.context = {};
req.context.isLoggedIn = req.isAuthenticated();
// req.locals.isAuthenticated = req.isAuthenticated();
next();
});
app.use('/dashboard', dashboard);
and this in the routes/dashboard route:
router.get('/', function (req, res) {
res.render('dashboard', { isLoggedIn: req.context.isLoggedIn });
});
Works - I can then see if the user is logged in by doing for example {{ isLoggedIn }}.
However when I uncomment the req.locals line in the first code snippet, I get a 500 error.
Two things to note:
Usually when your application needs to do something for a bunch of different pages, you want to setup a middleware function via app.use
Express has a res.locals variable whose properties will be included in any rendered template
With the above points in mind, you can construct something like the following:
app.use(function(res, req, next) {
res.locals.isAuthenticated = typeof(req.user) !== 'undefined';
next();
});
You then supply your additional template variables when your routes call res.render. For example:
app.get('/about', function(res, req) {
res.render('about', { 'testValue': 14} );
});
Your template will have access to both isAuthenticated and testValue.
I recommend you put some middleware in place before your route handlers but after passport's.
app.use(function(req, res, next) {
// Create a `context` object for use in any view.
// This allows the context to grow without impacting too much.
req.context = {};
// Assign your flag for authenticated.
req.context.isAuthenticated = typeof req.user !== 'undefined';
// Let the next middleware function perform it's processing.
next();
});
Then you can render each view with the context.
app.use('/', function(req, res) {
res.render('index', req.context); // Context is passed to view for usage.
});
This is all untested code.
You can do it as is already mentioned here ,but in this case you are going to check completely every request. Maybe you have got / you are going to have some pages that don't require any authentification and in this case you have to make some statement that will skip auth for that particular page or you can use something like this:
function checkUser(req, res, next) {
req.userAuth = (req.user !== undefined);
next();
}
app.post("settings", checkUser, doSomething);
app.post("administration", checkUser, doSomething);
app.post("index", doSomething); // Doesn't require any authentification
Or you can straight a way redirect a user
function checkUser(req, res, next) {
if (req.user === undefined) {
res.redirect("/login"); // res.render
}
else {
next();
}
}

Categories