is using res.locals decrease the performance in nodejs? - node.js

i was told that using res.locals will decrease the performance of my application , and it's better to attach variables on the request.
in my case i want to attach variables that are accessible only on the server side , and i don't want it to be sent back to the user , and i also came across sending the variable using
next(value) , what is the best approach for my case??
i have this middleware that gets the id of the user from jwt
jwt.verify(
accessToken,
process.env.ACCESS_TOKEN_KEY,
function (err, payload) {
if (err)
return res.status(401).send({
status: "failure",
response: "access token is not valid",
});
id = payload.id;
}
);
res.locals.userId = id;
next();
then this middleware that gets the role of the user based on the id
const RoleId = await sequelize.models.User.findByPk(res.locals.userId);
if (RoleId === 1) {
res.locals.title = "Admin";
next();
} else {
res.locals.title = "Customer";
next();
}

res.locals.xxx will have no different performance from setting res.xxx or req.xxx. No difference at all. So, without a specific reference to whomever said one would be slower than the other that somehow has more context, that's not correct.
And, res.locals are not sent to the client. Template engines doing server-side rendering by convention will look in res.locals to find variables that the template may reference that were not explicitly passed to res.render(). This is very useful for having common data (like a user's name) that you want the template to use, but don't want to have to manually pass to every single res.render() call.
This is entirely under your control since you, on the server, control both the template and the server-side rendering. This allows you to insert things into the page which is then sent to the user, but nothing goes in the template that you don't put there. So, res.locals is not a security risk unless you somehow give an outside agent control over your template (which would be subject to all sorts of security issues beyond just res.locals.
Another advantage of res.locals is that its an independent namespace that is entirely reserved for your use. No variable you use there will conflict with any existing functionality of the http class or Express or whatever web server engine you're using. The res object, on the other hand, has all sorts of existing methods and properties that you have to make sure you don't overwrite. So, res.locals is safer in that regard as it is specifically reserved for your own use. You can put any named property in there without any risk of conflict.
Passing a value to next(value) is how you abort further routing and immediately invoke your error handler, passing value to the error handler. This is not how middleware communicates data to downstream routing or rendering.
Both your code examples look like proper use of res.locals to me. That is exactly what it's for.
On a completely separate topic, your first code example has a coding error in it. You need to put these:
res.locals.userId = id;
next();
Inside the jwt.verify() callback. jwt.verify() is asynchronous. You won't have the id value until that callback is called. It should be like this:
jwt.verify(accessToken, process.env.ACCESS_TOKEN_KEY, function(err, payload) {
if (err) {
return res.status(401).send({
status: "failure",
response: "access token is not valid",
});
}
res.locals.userId = payload.id;
next();
});

Related

Why would an object's prototype be forgotten?

Introduction
So I am building a website with node.js, express, express-session, and sequelize.js. Once a user logs in, an instance of the Sequelize model User is created. In my route for user log-in (/auth/login), I have:
var user = (await User.findAll(
{where: {
username: username
}}))[0];
and I few lines down I assign that user to the session.
req.session.user = user;
And then I can persist any changes by simply calling the save method of req.session.user:
await req.session.user.save();
And indeed, if I add this line next:
console.log(Object.getPrototypeOf(req.session.user));
the output is [object SequelizeInstance:User]. So far so good.
Here is the problem
In another route (/users/myaccount/edit-bio) I am able to access the values of req.session.user. That is, the output of
console.log(req.session.user.username);
is seanletendre, as expected. But now when I call
await req.session.user.save();
all I get is the error message:
UnhandledPromiseRejectionWarning: TypeError: req.session.user.save is not a function
"That is weird," I thought, "isn't this the same object?" To investigate, I add the line:
console.log(Object.getPrototypeOf(req.session.user));
just as I did in the log-in route. And what is the output? It is: [object Object]. So it seems that somehow the prototype of req.session.user gets forgotten. I don't understand how this can be.
Is it possible to re-assign a prototype to a plain object?
Suspect A
Based on the comments to my question, I suspect that the prototype is lost when the session manager serializes req.session. It seems that, unlike I thought before,req.session does not point to the exact same session object for different requests. Each time a request ends, it serializes and stores req.session. Then upon receiving a new request with a cookie designating it as part of the same session, the session object is fetch from the session store.
This is how my session middleware is setup:
var session = require('express-session');
//
// yada, yada, yada
//
app.use(session({
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: false,
cookie: {secure: true}
}));
So what surprises me is that, even though I am using the default store MemoryStore, my sessions are still serialized.
My question now becomes: how can I prevent object serialization upon session store when using MemoryStore?
In express-session the method save() is exposed by the object session into the request object (docs), eg.:
req.session.save(callback)
Your code req.session.user.save() is wrong, the correct way is req.session.save(), diff.:
req.session.user.save();
-----------^^^^^
req.session.save()
The method save() isn't a Promise, you must pass a callback for wait for the result of the save:
req.session.user = user;
req.session.save(function(err) {
if( err ){
// session not saved
} else {
// session saved
}
})
you can transform it into a Promise (and await it), in this way:
const saveSession = (req) => {
return new Promise((resolve, reject) => {
req.session.save(function(err) {
if( err ){
reject(err);
} else {
resolve(true);
}
});
});
};
req.session.user = user;
await saveSession(req);
The method save() is automatically called at the end of the HTTP response if the session data has been altered. Because of this, typically this method does not need to be called.
UPDATE
I call req.session.user.save() because I want to save the Sequelize
Model instance into my database.
express-session use JSON.stringify() for serialize the session into a string and store into a session storage (more info). JSON.stringify() doesn't understand functions and only property are stored. For this reason, your save() function is lost.
Is it possible to re-assign a prototype to a plain object?
Technically you could re-assign the .prototype and .constructor. However, a model instance is quite a complex object. Try logging it to debugging console and you'll see it has lots of sub-objects of its own kind and also references to other more distant objects residing in a "more global" space (nothing like the plain object after deserialization (of JSON basically)). You'd have to re-instanciate them all. That is very hard to do.
My question now becomes: how can I prevent object serialization upon session store when using MemoryStore?
I am not experienced with express-session, but from what i read i think it basically works with parsing and stringifying JSON. So you can't prevent it when using express-session.
What you could do:
Just store the user.id to req.session.user_id. And User.findOne({ where: { id }) upon reloading the session. You then get a "real" sequelize model instance. And that instance you could assign to req.user, so that it's available throughout the request.
If you also hoped to avoid reloading the user's instance upon every request, then you are searching for a "caching" solution, which is different from "sessions". You'd then use a caching system like memcache, redis or others. However, there too, you probably won't get back a "real" instance of the model from the caching server.
If you have only one server where your application is running on, you could have a global.users variable, where you store all user instances via their id. For example "global.users[12345] = user". Like with 1. you'd retrieve it (or when not found initially load it) based on session.user_id
Some more thoughts: In your place, i'd also invest some time in figuring out, if sequelize allows to populate a new User() instance with data from JSON, and tricking sequelize into believing that this is not a new record.
Also, keep in mind, that depending on your application, the user's data in the database can change, while in your session or global.users you miss these changes.

Passport JS admin user verification

I have a Node, Express app with EJS at the front end.
I have this middleware function with passport that runs before all create, edit, delete routes.
function isLoggedIn(req, res, next) {
if (req.isAuthenticated()) {
if (req.user._id != "12345") {
res.redirect("/error");
}
return next();
}
res.redirect("/error");
}
The best I could come up with to verify that my admin user is the one trying to access the route is to check by user id in mongo db with req.user._id
Is there a better way to handle admin user access to routes and html components?
This looks essentially correct to me. It is important to note there are two layers: authentication and authorization.
Authentication is effectively a boolean: is the user authenticated? You have your function there req.isAuthenticated(). This might logically return a boolean, true or false, for whether or not the user is authenticated (ie: logged in).
Authorization could perhaps take many forms, but is effectively again a boolean: does this user meet the criteria to access this resource.
Authentication is usually well-served in a middleware, somewhere central that runs before "the endpoint", but authorization is not as simple because any endpoint could allow an operation or deny it, or it could respond differently depending on the user's privilege.
This whole conversation is perhaps quite deep in a roles & permissions discussion.
I think the answer is dependant on the app. Your app has two requirements: one, the user must be authenticated, and two, the user might need to be an admin.
The answer will be somewhere around: what is the most simple way to accomplish this?
In my opinion, you would consider the SOLID principles and note that you have one middleware, so it should have one responsibility: to check if the user is authenticated. Next, maybe you should have another middleware called isAdmin that runs for every endpoint that requires this extra condition. That's really all it is--an extra check. You shouldn't pollute your isLoggedIn middleware with that extra stuff because it makes that middleware less reuseable and less composable.
An isAdmin middleware would be a good idea, but it could also be a good idea to simply have it as a function inside every endpoint that requires that admin-check. Which way is better? Well first, which way is simpler. Which is less code but that is also still simple to understand.
Because this is roles and permissions, is there maybe a more robust way to keep track of which users are admins? If you have code that runs like if (req.user._id === 12345) {}, it requires special knowledge to remember this place in the code, so it is kind of brittle and "more likely" to fail. Maybe it would be a good idea to add a column to your users table for is_admin which could be null or 0 for every user except your user which could have 1. Then you could check if (req.user.is_admin) {}.
That might lead us to a middleware function like:
function isAdmin(req, res, next) {
if (req.isAuthenticated() && (req.user.is_admin === 1)) {
return next();
}
return res.redirect(403, "/error");
}
You could also do something like change that is_admin database column to instead something like role which could be 1 for every user except your admin users which could have maybe 2. That would allow you to do something like:
function hasAuthorization(req, res, next) {
if (req.isAuthenticated() && (req.user.role >= 2)) {
return next();
}
return res.redirect(403, "/error");
}
Such logic there can allow you to have increasing privilege roles: maybe 1 is regular, 2 is manager, 3 is admin, 4 is super-admin. If the user's role is less than 4, they don't have permission.
In my opinion, this idea of increasing privilege is great except the critical flaw might come later when you refactor your routes or your roles. You'd have to remember everywhere you had > 3 and change it to > 4. If you forget any, that is kind of a security flaw immediately, so I trust you understand my argument there.
Rather than seeing operators like < and >. I would rather see checks for specific roles, like:
if ((req.user.role === 'ADMIN') || (req.user.role === 'MANAGER')) {}
We have to keep coming back to the idea: what is the most simple? Is it simpler to make an isAdmin middleware and then group all your admin routes under the middleware? or is it simpler to put the authorization-check inside each route?
Check this example here:
import isAdmin from '../auth/isAdmin.js'
app.get('/admin', (req, res) => {
if (!isAdmin(req.user)) {
return res.redirect(403, '/error')
}
return res.render('admin')
})
This might be more work, but it's also potentially more fine-grained, so you have more control.
app.get('/foobars', (req, res) => {
if (isAdmin(req.user)) {
return res.json(/* all foobar records from all accounts */)
}
if (isManager(req.user)) {
return res.json(/* all foobar records from the user's account */)
}
return res.json({ error: 'Insufficient privileges for this operation' })
})
My final thought is that, you should have two functions: one checks if the user is authenticated, and one checks if the user is authorized. Then you can stack them together either in a middleware or in two middlewares, or in a route.
I also think you should find a more robust way to check if the user is your self. If you move your app from one computer to another, the user ID might change next time you populate your users table, so id isn't a strong way to latch onto your user.

Koa-router getting parsed params before hitting route

I'm using koa2 and koa-router together with sequelize on top. I want to be able to control user access based on their roles in the database, and it's been working somewhat so far. I made my own RBAC implementation, but I'm having some trouble.
I need to quit execution BEFORE any endpoint is hit if the user doesn't have access, considering endpoints can do any action (like inserting a new item etc.). This makes perfect sense, I realize I could potentially use transactions with Sequelize, but I find that would add more overhead and deadline is closing in.
My implementation so far looks somewhat like the following:
// initialize.js
initalizeRoutes()
initializeServerMiddleware()
Server middleware is registered after routes.
// function initializeRoutes
app.router = require('koa-router')
app.router.use('*', access_control(app))
require('./routes_init')
routes_init just runs a function which recursively parses a folder and imports all middleware definitions.
// function initializeServerMiddleware
// blah blah bunch of middleware
app.server.use(app.router.routes()).use(app.router.allowedMethods())
This is just regular koa-router.
However, the issue arises in access_control.
I have one file (access_control_definitions.js) where I specify named routes, their respective sequelize model name, and what rules exists for the route. (e.g. what role, if the owner is able to access their own resource...) I calculate whether the requester owns a resource by a route param (e.g. resource ID is ctx.params.id). However, in this implementation, params don't seem to be parsed. I don't think it's right that I have to manually parse the params before koa-router does it. Is anyone able to identify a better way based on this that would solve ctx.params not being filled with the actual named parameter?
edit: I also created a GitHub issue for this, considering it seems to me like there's some funny business going on.
So if you look at router.js
layerChain = matchedLayers.reduce(function(memo, layer) {
memo.push(function(ctx, next) {
ctx.captures = layer.captures(path, ctx.captures);
ctx.params = layer.params(path, ctx.captures, ctx.params);
ctx.routerName = layer.name;
return next();
});
return memo.concat(layer.stack);
}, []);
return compose(layerChain)(ctx, next);
What it does is that for every route function that you have, it add its own capturing layer to generate the params
Now this actually does make sense because you can have two middleware for same url with different parameters
router.use('/abc/:did', (ctx, next) => {
// ctx.router available
console.log('my request came here too', ctx.params.did)
if (next)
next();
});
router.get('/abc/:id', (ctx, next) => {
console.log('my request came here', ctx.params.id)
});
Now for the first handler a parameter id makes no sense and for the second one parameter did doesn't make any sense. Which means these parameters are specific to a handler and only make sense inside the handler. That is why it makes sense to not have the params that you expect to be there. I don't think it is a bug
And since you already found the workaround
const fromRouteId = pathToRegexp(ctx._matchedRoute).exec(ctx.captures[0])
You should use the same. Or a better one might be
var lastMatch = ctx.matched[ctx.matched.length-1];
params = lastMatch.params(ctx.originalUrl, lastMatch.captures(ctx.originalUrl), {})

node.js middleware and js encapsulation

I'm new to javascript, and jumped right into node.js. I've read a lot of theory, and began well with the practical side (I'm writing an API for a mobile app), but I have one basic problem, which has lead me to middleware. I've successfully implemented a middleware function, but I would like to know if the use I'm giving the idea of middleware is OK, and also resolve the original problem which brought me to middleware. My question is two-fold, it's as follows:
1) From what I could gather, the idea of using middleware is repeating a process before actually processing the request. I've used it for token verification, as follows:
Only one of my urls doesn't receive a token parameter, so
app.js
app.get('/settings', auth.validateToken, auth.settings);
auth.js
function validateToken(req, res, next){ //code };
In validateToken, my code checks the token, then calls next() if everything is OK, or modifies res as json to return a specific error code.
My questions regarding this are: a) Is this a correct use of middleware? b) is there a [correct] way of passing a value onto the next function? Instead of calling next only if everything is OK, is there a [correct] way of calling next either way, and knowing from inside the next function (whichever it is), if the middleware was succesful or not? If there is, would this be a proper use of middleware? This precise point brings me to my original problem, and part two of this question, which is encapsulating functions:
THIS PART WAS FIXED, SEE MY SECOND COMMENT.
2) I discovered middleware trying to simply encapsulate validateToken, and be able to call it from inside the functions that the get handlers point to, for example auth.settings.
I'm used to common, sequential programming, and not in javascript, and haven't for the life of me been able to understand how to do this, taking into account the event-based nature of node.js.
What I want to do right now is write a function which simply verifies the user and password. I have it perfectly written inside a particular handler, but was about to copy-paste it to another one, so I stopped. I want to do things the right way from scratch, and understand node.js. One of the specific problems I've been having, is that the error code I have to return when user and password don't match are different depending on the parent function, so I would need this function to be able to tell the callback function "hey, the password and user don't match", so from the parent function I can respond with the correct message.
I think what I actually want is to write an asynchronous function I can call from inside another one.
I hope I've been clear, I've been trying to solve this on my own, but I can't quite finish wrapping my head around what my actual problem is, I'm guessing it's due to my recent introduction to node.js and JS.
Thanks in advance! Jennifer.
1) There is res.locals object (http://expressjs.com/api.html#res.locals) designed to store data local to the request and to pass them from one middleware to another. After request is processed this object is disposed of. If you want to store data within the session you can use req.session.
2) If I understand your question, you want a function asynchronously passing the response to the caller. You can do it in the same way most node's functions are designed.
You define a function in this way:
function doSomething(parameters, callback) {
// ... do something
// if (errorConddition()) err = errorCode();
if (callback) callback(err, result)
}
And the caller instead of using the return value of the function passes callback to this function:
function caller(req, res, next) {
//...
doSomething(params, function(err, result) {
if (! err && result) {
// do something with the result
next();
} else {
// do something else
next();
// or even res.redirect('/error');
}
});
}
If you find yourself writing similar callback functions you should define them as function and just pass the function as parameter:
//...
doSomething(param, processIt);
function processIt(err, result) {
// ...
}
What keeps you confused, probably, is that you don't treat functions as values yet, which is a very specific to JavaScript (not counting for languages that are little used).
In validateToken, my code checks the token, then calls next() if everything is OK, or modifies res as json to return a specific error code.
a) Is this a correct use of middleware?
b) is there a [correct] way of passing a value onto the next function?
Yes that is the correct way of using middleware, although depending on the response message type and specifications you could use the built in error handling of connect. That is in this example generate a 401 status code by calling next({status:401,stack:'Unauthorized'});
The middleware system is designed to handle the request by going through a series of functions until one function replies to the request. This is why the next function only takes one argument which is error
-> if an error object is passed to the next function then it will be used to create a response and no further middleware will be processed. The manner in which error response is created is as follows
// default to 500
if (res.statusCode < 400) res.statusCode = 500;
debug('default %s', res.statusCode);
// respect err.status
if (err.status) res.statusCode = err.status;
// production gets a basic error message
var msg = 'production' == env
? http.STATUS_CODES[res.statusCode]
: err.stack || err.toString();
-> to pass values down the middleware stack modifying the request object is the best method. This ensures that all processing is bound to that specific request and since the request object goes through every middleware function it is a good way to pass information down the stack.

How do I pass variables from Connect's middleware into app.get-action?

I would like to create kind of a before filter which allows me to make the current user available in all actions. The followint approach works well and I didn't even need to declare a global variable:
app.use(function(req, res, next){
if(req.session.user_id){
/* Get user from database
and share it in a variable
that can be accessed frooom ...
*/
User.find({ /* ... */ }, function(err, users){
if(users.length == 1){
req.current_user = users[0];
}
next();
});
}
else{
next();
}
});
app.get('/', function(req, res){
// ... here!!
console.log(req.current_user);
res.render('index', {
current_user: req.current_user,
});
});
But I'm still unsure if it is okay to manipulate req because I don't know if it's right to change something that's not owned by me? Is there a better way to do this?
Go right ahead and tack on properties to req! When I was first starting out with Node.js and JavaScript, this felt very odd to me too (coming from a predominately C++ background). It is, however, quite natural given JavaScript's prototypical object model. After you get comfortable with it, you'll realize that you can do powerful things in succinct code.
I'm the developer of Passport (mentioned by the previous commenter). If you are planning on developing middleware that can be reused across apps, my advice is to pay a bit of attention to how you name the properties that you add to req or res, to avoid any potential conflict with other middleware in the same application.
For example, Passport sets the user at req.user, but gives an option to change that (so an app can say set it at req.currentUser, for example.). Internal, private variables are attached to a req._passport property.
It's a common approach to extend req with session or user object
For example see these examples:
Passport, a popular authentication library https://github.com/jaredhanson/passport/blob/master/lib/passport/strategies/session.js
Connect middleware for cookie session https://github.com/senchalabs/connect/blob/master/lib/middleware/cookieSession.js

Resources