Will a Lambda function using Express reload it's middleware for each request? - node.js

TLDR: Will the middleware that gets called by app.use be recalculated for each individual request in Express?
I have a Lambda function that uses the following function as its middleware to inject the user into every request:
async function(req, res, next) {
try {
const IDP_REGEX = /.*\/.*,(.*)\/(.*):CognitoSignIn:(.*)/;
const authProvider =
req.apiGateway.event.requestContext.identity
.cognitoAuthenticationProvider;
const [, , , userId] = authProvider.match(IDP_REGEX);
const cognito = new AWS.CognitoIdentityServiceProvider();
const listUsersResponse = await cognito
.listUsers({
UserPoolId: process.env.AUTH_SAYM_USERPOOLID,
Filter: `sub = "${userId}"`,
Limit: 1,
})
.promise();
const user = listUsersResponse.Users[0];
req.user = user;
next();
} catch (error) {
console.log(error);
next(error);
}
}
My question is: Will this middleware run once for each individual user? Or will this code run only every time the Lambda function "goes to sleep"? What I mean is, will there be a bug, if the Lambda function gets pinged often and never goes idle again that each request will have the user who made the first request? Or does the middleware calculate for each individual request?

This middleware will run upon every request and this has nothing to do with Lambda. Once the requests reaches your Express-based Lambda function, it's just going to act as a regular Express server.
The middleware's responsibility is to intercept every HTTP call, regardless of where it's running.
The only thing I see here is to move the declaration of const cognito = new AWS.CognitoIdentityServiceProvider(); out of your middleware's scope, so the instance of this object is cached for as long as the container lives. You will gain a couple of microseconds by doing so (which may be irrelevant, but is indeed a good practice).
You have to keep in mind though that since modules in Node.js are singletons, anything required/declared in "global" scopes (outside a given function, for example) is going to be re-used in future invocations to the same running container. Use this in your favour but also be careful with unintentionally caching things you don't want to.

Related

Node.js REST API - URI Sanitizing?

I would like to require pages in my Node.js server based on the requested URI.
However I concern that this could be a severe security issue since user can inject some malicous chars into the url, something like ../../ and reach to my root server point and reveal all of the code.
So just like throwing a bottle of water to a big fire, I have eliminated the option to send . to the request.
This is not a silverbullet, probably :)
Maybe is there some standard/best practice/guide or keypoints about URI sanitizing in REST API based on Node.js?
Edit - here the code uses the require
// app.js
app.use(require('./services/router')(app));
// router.js middleware
function router(app) {
return function(req, res, next) {
try {
// checking for . in the url
if (req.url.indexOf(".")!=-1) cast.badRequest();
// req.url.split('/')[2] should be customers, users or anything else
require('../../resources/' + req.url.split('/')[2] + '/' + req.url.split('/')[2] + '-router')(app);
next();
} catch(err) { cast.notFound(); }
}
}
module.exports = router;
// rides-router.js (this could be users-router.js or customers-router.js)
module.exports = function(app) {
// GET ride - select a ride
app.get("/v1/rides/:id", dep.verifyToken(), require('./api/v1-get-ride'));
// POST ride - insert a new ride
app.post("/v1/rides", dep.verifyToken(), require('./api/v1-set-ride'));
app.use((req, res, next) => {
cast.notFound();
});
}
You asked how to do it safer. My recommendation is that you put all the resources in an array and run all the app.use() statements with one loop that pulls the resource names from the array at server startup.
I don't like running synchronous require() during a request and I don't like loading code based on user specified characters. Both are avoided with my recommendation.
// add routes for all resources
const resourceList = ['rides', 'products', ...];
for (let r of resourceList) {
app.use(`/${r}`, require(`./resources/${r}/${r}-router`));
}
This seems like less code and 100% safe and no running of synchronous require() during a request.
Advantages:
Fully whitelisted.
No user input involved in selecting code to run.
No synchronous require() during request processing.
All routes installed at server initialization time.
Any errors in route loading (like a missing route file) occur at server startup, not during a user request.

How to properly use dataloaders across multiple users?

In caching per request the following example is given that shows how to use dataloaders in express.
function createLoaders(authToken) {
return {
users: new DataLoader(ids => genUsers(authToken, ids)),
}
}
var app = express()
app.get('/', function(req, res) {
var authToken = authenticateUser(req)
var loaders = createLoaders(authToken)
res.send(renderPage(req, loaders))
})
app.listen()
I'm confused about passing authToken to genUsers batch function. How should a batch function be composed to use authToken and to return each user corresponding results??
What the example is saying that genUsers should use the credentials of the current request's user (identified by their auth token) to ensure they can only fetch data that they're allowed to see. Essentially, the loader gets initialised at the start of the request, and then discarded at the end, and never recycled between requests.

Next.js with MySQL/Mongo backend

I have an existing Node.js/Express app which connects to 2 separate databases, it has a MySQL DB for all the relational and a MongoDB store for the non-relational vertical data.
It uses Sequelize and Mongoose and works absolutely swimmingly.
I've been looking at Next.js today and I'm pretty impressed, one of my pet peeves with React is actually how much bootstrapping there is and how much code it takes to achieve something simple. Next.js seems to solve some of those issues for me, so I'm willing to embrace it.
First issue - Is it possible to connect Next.js to existing DB's and read their objects directly in the view?
e.g. ./server.js:
const mongoDb = mongoose.connect(configDB.url); // MongoDB connection
const models = require('./models'); // Sequelize connection
app.prepare().then(() => {
server.use((req, res, next) => {
req.mongodb = mongoDb
req.mysqldb = models
// Logging req.mysqldb/req.mongodb at this point gives the correct result.
next()
});
server.get('*', (req, res) => {
return handle(req, res)
})
})
./pages/index.js:
Index.getInitialProps = async function(req) {
console.log(req.mongodb);
console.log(req.mysqldb)
// Example of what I want: req.mysqldb.users.findAll()....... to populate collection for this view
}
When the console statements are executed in the index.js page, they are logged as undefined.
Ideally I want to use the objects/ORM layer directly in the next.js templates, I do not want to have to call my own API internally, it seems like a huge waste of resources!
Any help, greatly appreciated.
Just for future reference. getInitialProps gets passed in an object with one of the keys being req. So you're meant to do something like the following instead
// add the curly braces around req
Index.getInitialProps = async function({ req }) {
// code
}
This is known as Function Parameter Destructuring and was introduced in ES6. What this accomplishes is similar to the following code
Index.getInitialProps = async function(_ref) {
var req = _ref.req;
}
Meaning, it takes the value of req of the object that gets passed and uses that value.
Well apparently by the time the request gets to the template it has changed a bit! Namely, it is nested within another request object.
req.req.mongodb and req.req.mysqldb both work fine :).

per-request session in meteor server?

I am adding an auth layer and I think I have it figured out except for one tricky detail.
My Meteor app doesn't have any routes but I've added a hook into the connect middleware so that the "/" route errors if there isn't a correct API token. If the token is okay then I call next() to forward the route to Meteor.
The problem is that, depending on the token, I need to set server-side parameters for the connection, and I don't know how to do this. For example, say I have a static list of API keys mapped to permission levels. If a user sends a request with "ADMIN_API_KEY" then I would like to set Session.permission_level = "admin" for use by the Meteor server's functions. Session is just for the client in Meteor, though.
# this code's in coffeescript
WebApp.connectHandlers.use '/', (req, res, next) ->
validator = new RequestValidator(req, next)
validations = [
"valid_namespace",
"only_https"
]
error = validator.validate(validations)
next(error)
# <<<<<<<<<<<<<<<<<<<<<<<<
# Here I want to set some config option which can be
# read by the server in the same way it can read things like
# Meteor.user()
In Rails I would just say session[:permission_level] = "admin". But it seems to not work this way in Meteor.
By the way, I am not using a Routing package yet in Meteor, though if that would make this easier than I would.
I'm not sure about Session I've been doing something like
import { DDP } from 'meteor/ddp';
import { DDPCommon } from 'meteor/ddp-common';
export const authMiddleware = (req, res, next) => {
const userId = identifyUser(req); // parse the request to get the token you expect
if (!userId) {
return next();
}
DDP._CurrentInvocation.withValue(new DDPCommon.MethodInvocation({
isSimulation: false,
userId,
}), () => {
next();
// in that context, Meteor.userId corresponds to userId
});
};
for my REST api and that works well regarding the user Id and being able to call Meteor function that should be invoke in a DDP context, like Users.find(...).

Node.js variables for current request only?

I am very new to Node.js, and I was wondering if that, except for session(), I could use a "storage" to store variables for the current request?
I have an API which is based on an Authorization header, and a pool of valid tokens stored in Redis.
Therefore I don't have a session and don't want to.
But I would like to store variables for further use during this request. For example, I would like to store the user_id corresponding to the token found in Redis, so that I can use it wherever I want.
If I do something like:
app = express();
app.user_id = 1;
Is it ok, or will my user_id become global to all requests handled by the app? (in short: is the app instanciated for each request handled by the server, or is it persistent?)
If this is not ok, how could I achieve something like this without sessions?
Thank you for any help :)
The app handles all requests, and would only be created once on startup, but req lives for only the lifetime of the request. Keep in mind that the req in Express is just an object, and as such, can be assigned values. So if you wanted to allow the controller to have access to some value (similar to sessions), you could do something like this:
var express = require('express');
var app = express();
// middleware that assigns a value '123' to 'req.user_id'
app.use(function(req, res, next) {
req.user_id = 123;
next();
});
// controller which responds with the 'req.user_id'
app.get('/hello', function(req, res){
res.send('req.user_id: ' + req.user_id); // responds with req.user_id: 123
});
app.listen(3000, function() {
console.log('Listening on port 3000');
});
In the above example, the middleware that I created assigns a value to the request, called user_id. This value lives for the life of the request. You could do a similar thing to assign a dynamic value to the req so that you can access it via your controllers.

Resources