nodeJS code pattern => Express middleware/Oauth2/Passport - node.js

I inherited a codebase where it looks like they run middleware in node with the following pattern for Oauth2 passport strategy
module.exports = function (router) {
router.get('/', function (req, res, next) {
passport.authenticate('type', object, function(err, info) {
//pass info object to next middleware
})(req,res,next) <---where does this go?!?
})
}
From my current understanding of the code base, this is actually the last function call in the middleware chain, so could I just add a piece of middleware to the bottom?
Does this sound like the right idea?
And just to clarify what I'm trying to do:
pass data from Oauth callback through middleware function by attaching it to the req
perform DB business logic (create or lookup account)
login with JWT
redirect

This appears to be the "custom callback" method of using passport's authenticate function. If you look at the documentation you can see how they expect it to be used. That said, I don't know what that second argument is supposed to be doing (the object) - it looks like a variable, but I don't see it defined anywhere, and I'm not sure the authenticate method takes arguments in that manner. Also, the custom callback takes three arguments: err, user, and then info... which might trip you up.
Okay, so now to your actual question of "could I just add a piece of middleware to the bottom?" Sort of? The fact is, you're in a routing middleware at that point. If it matches and auth is successful, then you should do whatever code for that route is required inside the custom callback. That's the point of this way of doing things. Alternatively you could use passport.authenticate as a piece of middleware itself (it returns a middleware function usable in the CommonJS pattern.
If you don't want to change up the code, then you could just do this:
module.exports = function (router) {
router.get('/', function (req, res, next) {
passport.authenticate('PICK A VALID TYPE', function(err, user, info) {
// this custom callback will be executed once auth completes
// (either successfully or not
// put code in here to perform DB business logic, login, and redirect
})(req,res,next); <--- this executes the passport.authenticate middleware
})
};

Related

Variable not getting through to middleware function

I have a bug which has made me realize I clearly missing something important about express middlewares. I have something like this:
const checkAuth = ({ allowedRoles = [], targetUserId }) => {
return async (req, res, next) => {
console.log(targetUserId);
//Do checks
if(!allowedRoles.includes(req.user.role) return
targetUserId = req.params[targetUserId]
if(!allowedUsers.includes(targetUserId) return
return next();
};
};
app.get(
"/activity/:id",
checkAuth({ allowedRoles: ["locationAdmin", "orgAdmin"], targetUserId: "id" }),
async function (req, res, next) {
//Do things
});
});
The idea behind this middleware is the 'targetUserId'variable tells it which request parameter to look in for a value, then it can choose to authorize the request with the value it finds there. The middleware actually exists in it's own file/module if that makes any difference.
Here's what I expect to happen:
User loads url /activity/23 > "id" is logged
User loads url /activity/23 > "id" is logged
and so on until the end of time...
What actually happens:
User loads url /activity/23 > "id" is logged
User loads url /activity/23 > 23 is logged
User loads url /activity/23 > undefined is logged
User loads url /activity/23 > null is logged
item 4 will then repeat forever
My expectation is that each time checkAuth is called, there should be nothing in memory from it's last call. It's taken me a couple of hours to get my head around this, but what seems to be happening, is after the value of targetUserId is set within the middleware, the variable remains set in memory, and isn't changed when it gets called again with "id". This explains why the value logged to the console changes 3 times on repeat requests for the same page.
But why does it do this and how can I fix it?
The way your code is structured, checkAuth() is called once when your code is first run. That captures the arguments you passed it and then makes those available to the middleware function that checkAuth() returns and that middleware function is passed to Express where it is registered as middleware for this request handler.
That middleware function you return is then called by Express for each incoming request that matches this request handler. The arguments you passed to checkAuth() are available to that middleware function each time it is called.
If some code changes those arguments (like code inside the middleware itself changes an object or property passed), then those changes are persistent (in a closure) and future middleware will see those changes. So, if you need to modify those arguments for the purposes of one request, then you should make a copy of the data or make the modifications in a variable that is local to the internal middleware function.

How to work with implied function parameters in NodeJS

In my website's routes file, I have a function like this:
router.post('/', ctrl1.validate, ctrl2.doSomething)
With the validate function looking like this:
function(req,res,next){
var errors = validator.checkForm('myForm')
if(errors){
res.redirect("/")
}else{
next()
}
}
If I want to pass parameters into the validator function (like the name of forms I want to validate) besides the implied req,res,next, how is that done? I have tried ctrl1.validate(formName) and ctrl1.validate(formName, req,res,next) with function(formName, req,res,next) in the controller, and neither work.
The ideal solution would be to identify what form you're working on from the data passed with the request in the first place. You don't show what that is, so we don't know exactly what to recommend or if that is feasible in this case.
If you can't do that and want to have a generic function that you can use as a request handler in multiple places and you want to pass a parameter to it that is different in each of the different places you use it, then you need to create a function that returns a function.
router.post('/', ctrl1.validate("someFormName"), ctrl2.doSomething)
// definition of ctrl1.validate
validate: function(formName) {
// return a request handler that will knkow which formName to use
return function(req,res,next){
var errors = validator.checkForm(formName)
if(errors){
res.redirect("/")
} else {
next()
}
}
}
When you first call this method, it returns another function that is your actual request handler. That inside function then has access to both req, res, next from the request and has access to the formName that was originally passed in.

express js - How is one http request different from other?

I've been working on creating a better architecture for rest api in express and node. Let's say I have 3 methods in my route middleware -
router.post('/users/:id', [
UserService.getUserById,
UserController.setUser,
MailerService.sendSubscriptionMail
]);
I am setting up req.session.user in call to UserService.getUserById and then using that to set req.session.result in UserController.setUser. Now I am sending a mail to this user using the data stored in req.session.result.
UserService -
exports.getUserById = function(req, res, next) {
.
.
req.session.user = data;
.
.
};
module.exports = exports;
UserController -
exports.setUser = function(req, res, next) {
.
.
req.session.result = req.session.user;
.
.
};
module.exports = exports;
MailerService -
exports.sendSubscriptionMail = function(req, res, next) {
// Using req.session.result here to send email
};
module.exports = exports;
Now I have two questions regarding above process -
(a) Is there any chance that a new http req to another route (which also has these kind of methods which can modify req.session) can modify the req.session.result and MailerService.sendSubscriptionMail does not get the data which it needs to send to the user or will that req object will be completely different from this one in the memory?
(b) Is there any other method to transfer data between middleware rather than setting up req object?
Is there any chance that a new http req to another route (which also
has these kind of methods which can modify req.session) can modify the
req.session.result and MailerService.sendSubscriptionMail does not get
the data which it needs to send to the user or will that req object
will be completely different from this one in the memory?
The req object is specific to this request. That object cannot be changed by another request. But, if the session in req.session is a common shared sesison object that all requests from that particular user share, then req.session.result could be changed by another request from that user that is getting processed at around the same time (e.g. interleaved within various async operations).
If you want to make sure that no other request from this user could change your result, then put it in req.result, not req.session.result because no other requests will have access to req.result.
Is there any other method to transfer data between middleware rather
than setting up req object?
The req or res objects are the right places to share info among middleware handlers for the same request as they are unique to this particular request. Be careful with the session object because it is shared among multiple requests from the same user.
Another possible way to share the data among your three handlers is to make a single middleware handler that calls all three of your middleware handlers and then share the data within that single function (e.g. passing it to the other handlers).
For example, you could change the calling signature of your 2nd two methods so you can get the data out of setUser() and then pass it directly to sendSubscriptionMail() without using the req or session objects to store it.
router.post('/users/:id', [UserService.getUserById, function(req, res, next) {
UserController.setUser(req, res, function(err, result) {
if (err) return next(err);
MailerService.sendSubscriptionMail(result, req, res, next);
}]);
});

ExpressJS authorizing users with middleware?

Let's say I want to restrict the access of a resource to a certain group of people who meet some conditions. What I'm doing right now is defining an authorization middleware that checks if the req.user is meeting those conditions.
module.exports.requiresCondition = function(req, res, next){
Model.findOne({condition: condition}, function(err, model){
//check if various conditions are met. If not, return 401
res.locals.model = model;
return next();
}
}
The problem I have with this is that I can't choose what data to project because the routes that come after might use different parts of the model. This means I have to get the whole model every time, which becomes inefficient as the documents get larger and larger. Of course, I can just query once in the middleware with the keys I need to authorize and query again in the actual controller, but that doesn't seem particularly efficient either. Is there a better way to authorize users?
If different routes require different data you can modularize your middleware layer. composable-middelware https://www.npmjs.com/package/composable-middleware is perfect for that.
So let's say you have couple of routes:
import compose from 'composable-middleware';
router.get('/type1/:id', compose().use(Auth.hasAppAccess()).use(TypeMiddleware.getType1()), ctrl.doType1);
router.get('/type2/:id', compose().use(Auth.hasAppAccess()).use(TypeMiddleware.getType2()).use(TypeMiddleware.evenMoreOperations()), ctrl.doType2);
Then your middleware itself
import compose from 'composable-middleware';
export function isAuthenticated() {
return compose()
.use(function(req, res, next) {
//do your stuff here
})
}
//similarly for other middelware functions

nodejs express profile property in request

I got very confused for one usage:
In the route file:
app.param('userId', users.load);
And the users.load function:
exports.load = function (req, res, next, id) {
var options = {
criteria: { _id : id }
};
User.load(options, function (err, user) {
if (err) return next(err);
if (!user) return next(new Error('Failed to load User ' + id));
req.profile = user;
next();
});
};
Here, route should have the userId to response but why does the author use req.profile here. profile is not a property.
Anyone can help?
Thanks.
What the code does is this: for routes that have a userId parameter (that is, routes that look similar to this: /user/:userId), Express will call the load() function before the route handler is called.
The load function loads the user profile belonging to the userId from the database, and adds it to req as a newly created property req.profile.
The .profile property name is arbitrarily named by the author and demonstrates the fact that it's perfectly valid to add properties to req (or res, for that matter, but convention is to add these properties to req).
In the route handler, you can then use req.profile. It's basically a way of propagating data from middleware and app.param() implementations to other parts of the route handling.
the line req.profile = users; think of it this way, 'i want to take all the powers of the users and paste them to req.profile' why? remember this part is sort of a middleware if you want to target any of the read, update and delete code it has to pass through here, it only makes sense if it involves the req, because you are practically requesting to access the said pages (read, edit and delete or any other:userId page) now the profile name doesn't matter you could use any name but its sort of a convention in the community to use the profile name.

Resources