Node.js middleware organization and parameter validation - node.js

I'm building an express app and I'd like to know how fancy I can get with middleware. Roughly, I want to accomplish the following with middleware.
Done:
Add requestId to all routes
Authenticate request
Check whether a user has access to a given resource (apart from
authentication)
Not done:
A) Validate parameters for a given route
B) Organize middleware in a sane way if it differs from route to route,
and 3 middlewares are called routinely per route
I have defined my middleware in a separate file, and import it into app.js like so:
var middleware = require('./middleware');
var requestId = middleware.requestId;
var authenticate = middleware.authenticate;
To apply it to all routes I add it to express config:
var app = express.createServer();
app.configure(function () {
app.use(express.logger());
app.use(express.cookieParser());
app.use(express.bodyParser());
app.use(requestId); // add requestId to all incoming requests
});
And for route specifics, I add it as an app.get argument:
var routes = require('./v1/routes');
app.get("/v1/foo", routes.foo);
app.get("/v1/bar", authenticate, routes.bar);
Problem A
I'd love to have middleware that I could use to check parameters
validate('x','y','z')
And use it like so for a given route:
app.get("/v1/bar", authenticate, validate('x','y','z'), routes.bar);
Is there a good way to do this? Or should I just be validating on per route basis inside the route definition files?
Problem B
Is there a better way to organize and use my middleware that I should consider?
Update
I'm looking for a way to validate parameters that change a lot between routes. The below obviously don't work- I cannot pass params into the middleware- but is there way where I can define middleware that does this and call it as I've said above?
var validateParams = function (req, res, params, callback) {
// Make sure the required parameters are in the request
console.log('checking for params '+params);
for (var i = 0; i < params.length; i++) {
var param = params[i];
if(!(param in req.query)){
logger.info('cannot find param ['+param+'] in req: '+JSON.stringify(req.query));
res.writeHead(400, {
"Content-Type": "application/json"
});
var out = {
"err": "request missing required parameters"
};
res.end(JSON.stringify(out));
return;
}
}
callback();
}

Problem A
app.get("/v1/bar", authenticate, validate, routes.bar);
function validate(req,res,next){
//Get all parameters here by req.params and req.body.parameter
//validate them and return.
if(validation_true)
next()
}
Problem B
You can use middleware in a way that you don't always need to call authenticate and validate they are called automatically. But that can lead to a mess, for ex. Your middleware then would run on every call, so for SIGNUP/REGISTER there is no point running authenticate.
With validate, sometimes you would need to validate email, sometimes phone no. so both cannot go along.
So using them separate on every call seems the BEST way to me.

You can use express-validation to validate body, query, params, headers and cookies of a request. It responds with errors, if any of the configured validation rules fail.
var validate = require('express-validation'),
Joi = require('joi');
app.post('/login', validate({
body: {
email: Joi.string().email().required(),
password: Joi.string().regex(/[a-zA-Z0-9]{3,30}/).required()
}
}), function(req, res){
res.json(200);
});
This will check if the email and password body params matches the validation rules.
If validation fails it will respond with the following error.
{
"status": 400,
"statusText": "Bad Request",
"errors": [
{
"field": "password",
"location": "body",
"messages": [
"the value of password is not allowed to be empty",
"the value of password must match the regular expression /[a-zA-Z0-9]{3,30}/"
],
"types": [ "any.empty", "string.regex.base" ]
}
]
}
You can also check my repo express-mongoose-es6-rest-api for complete integration.

You could also use a higher-order function (function that returns a function). Thereby passing an array of endpoint specific params to check.
module.export = Class RequestValidator {
static validate(params) {
return function(req, res, next){
for(const param of params) {
validateYourParams here...
if (validation fails) {
return next(new Error());
}
}
next();
}
}
}
And within your routeDefinition you can now call the validation middleware and pass route specific arguments to it.
const RequestValidator = require('your-validation-middleware');
const controller = require('your-controller');
app.post('/path')
.RequestValidator.validate(
[{
name: 'paramName',
type: 'boolean'
},
{
name: 'paramName2',
type: 'string'
}
])
.Controller.handleRequest;

Related

How to obtain the token from the current user with jwt/express/node

I have a controller that receives an user that is trying to login via form. When all validations are checked, the user will be logged in and a token will be created in the following way:
const token = jwt.sign({userId: user._id}, config.secret ,{expiresIn: '24h'})
res.json({success: true, message: 'Sesión iniciada', token: token, user: {email: user.email}})
However, how do I access this token from another controller? I've seen that a good approach would be to create a middleware that intercepts such token, but I don't really know how to accomplish this.
I'd be happy only knowing how to get the token tho. I'm kinda new and I'm taking very small steps.
You should setup your client requests to send such token as #Vahid said.
Here's an example with axios
const instance = axios.create({
baseURL: 'https://some-domain.com/api',
// From the docs:
// `transformRequest` allows changes to the request data before it is sent to the server
// This is only applicable for request methods 'PUT', 'POST', 'PATCH' and 'DELETE'
// The last function in the array must return a string or an instance of Buffer, ArrayBuffer,
// FormData or Stream
// You may modify the headers object.
transformRequest: [function (data, headers) {
headers['Authorization'] = localStorage.getItem('jwt')
return data;
}],
})
export default instance
In case you also need GET request you can add:
export setAuthToken = (token) => {
instance.defaults.headers.common['Authorization'] = token;
}
Although you'll need to call it every time your JWT is renewed.
After that, you could catch it using the Middlewares to decode the token from the headers
app.use((req, res, next) => {
const authToken = req.headers['Authorization']
if(authToken) {
try {
const decoded = jwt.verify(authToken, config.secret)
req.user = decoded.userId
// Hopefully
// req.user = getUserById(decoded.userId)
next()
} catch(e) {
// Handle Errors or renewals
req.user = null
// You could either next() to continue or use 'res' to respond something
}
} else {
// Throw 403 if should be authorized
res.sendStatus(403)
}
})
This way you should be able to access req.user on any route defined after your middleware.
Eg:
app.post('/me', (req, res) => {
res.send(req.user)
})
Note that this is just one example of a global middleware. In other cases, you should be able to create custom middlewares based on which routes you want to protect or with which amount of permissions.

How to add custom middleware to express-openapi-validator using Swagger 3

I've got a Node app using express-openapi-validator that takes a an api spec file (which is a .yml file), with request and response validation. The express-openapi-validator package routes the request to a handler file (defined in the spec). This is what one of the handlers might look like:
function getUsers(req, res) {
const { 'x-user-id': userId } = req.headers
res.status(200).json(`Your userId is ${userId}`)
}
I've got an API key feature, where users can get a new API key, and the other endpoints that need the caller to have the API key in the request headers to validate the request.
I know it should be possible to use middleware to validate the request, but I can't figure out how to use custom middleware with the express-openapi-validator package on select endpoints.
For eg:
GET /apikey = does not require api key
GET /resource = requires api key
How do I configure this?
Here's what the openapi validator code in my app.js looks like:
new OpenApiValidator({
apiSpec,
validateResponses: true,
operationHandlers: path.join(__dirname, './handlers'),
})
.install(app)
.then(() => {
app.use((err, _, res) => {
res.status(err.status || 500).json({
message: err.message,
errors: err.errors,
});
});
});
I actually ended up finding a solution for this myself.
First of all, I'm using version 4.10.5 of express-openapi-validator, so the code above is slightly different.
Here's what it looks like now:
// index.js
app.use(
OpenApiValidator.middleware({
apiSpec,
validateResponses: true,
operationHandlers: path.join(__dirname, './handlers'),
validateSecurity: {
handlers: {
verifyApiKey(req, scopes) {
return middleware.verifyApiKey(req)
},
bearerAuth(req, scopes) {
return middleware.verifyToken(req)
}
}
},
}),
);
app.use((err, req, res, next) => {
res.status(err.status || 500).json({
message: err.message,
errors: err.errors,
});
The way I ended up using middleware in my routes is below:
I've added a securitySchemes section in my swagger.yml file, like so:
components:
securitySchemes:
verifyApiKey:
type: apiKey
in: header
name: x-api-key
bearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
There's a bit more information about it here: https://swagger.io/docs/specification/authentication/
On each route that needs the middleware, I'm adding a security section, like so:
/team:
post:
security:
- bearerAuth: []
description: Create a new team
operationId: createTeam
x-eov-operation-id: createTeam
x-eov-operation-handler: team
As you can see in my code above (in the index.js file), I've got a validateSecurity key, with a handlers key that then has the correlating keys that are in my swagger.yml (verifyApiKey and bearerAuth). These functions get the request and scope to check if they're valid. These functions return a boolean value, so true means that the middleware lets the request through, and false means a 403 response will be returned.
validateSecurity: {
handlers: {
verifyApiKey(req, scopes) {
return middleware.verifyApiKey(req)
},
bearerAuth(req, scopes) {
return middleware.verifyToken(req)
}
}
},
Please respond if I've got anything above wrong, or if the explanation can be clearer. If you have questions, please post them below.
You can simply pass array of handlers instead of just 1 function, like in express.
So in you code, the getUsers function that probably is what the x-eov-operation-id refers to, would be an array of 2 functions:
const getUsers = [
apiKeyMiddleware,
(req, res) => {
const { 'x-user-id': userId } = req.headers
res.status(200).json(`Your userId is ${userId}`)
}
];
I was in a similar situation as you, using OpenAPI/Swagger packages like that limited my ability to add specific middleware per endpoint, so my solution was I created an npm module called #zishone/chaindler.
You can use it like this:
const { Chain } = require('#zishone/chaindler');
function getUsers(req, res) {
const { 'x-user-id': userId } = req.headers
res.status(200).json(`Your userId is ${userId}`)
}
function postUsers(req, res) {
// ...
}
function mw1(req, res, next) {
next()
}
function mw2(req, res, next) {
next()
}
module.exports = {
getUsers: new Chain(mw1, mw2).handle(getUsers),
postUsers: new Chain(mw1).handle(postUsers)
}
Basically it just chains the middlewares then calls them one by one then call the handler/controller last.

Intercepting headers on NodeJS (Express) before routing in a global scope

I've a code like this on my server.js
var session = {
v1: require('./routes/v1/session')
}
app.post('/v1/session/login', session.v1.login);
app.get('/v1/session/logout/:uuid', session.v1.logout);
var modify = {
v1: require('./routes/v1/modify')
}
app.put('/v1/modify/:uuid/password', modify.v1.password);
app.put('/v1/modify/:uuid/mobile', modify.v1.mobile);
app.put('/v1/modify/:uuid/email', modify.v1.email);
All the cases above I am sending an Authorization header with a token, except for Login because you will receive a token after the login.
So, in all of my routes, I need to call a routine to validate the token and then perform the operations. Like this:
exports.logout = function (req, res) {
var auth = req.headers.authorization;
global.utils.validateToken(auth).then(function(uuid) {
// Code here
}
}
But I am pretty convinced that there is a better way to do that. Something like execute an authorization check for each server request before call the router.
Is such thing possible ? And if yes: Can I define exceptions (for example...in case of Login, I don't need to check the Authorization) ?
thanks !
There are multiple ways to do so, I am stating three approaches, you can go with whichever you like.
So basically, you need to make a middleware which should not be called while login.
what you will do is, make a middleware as given below:
var middleware = {
authTokenValidator = function(req, res, next) {
var auth = req.headers.authorization;
global.utils.validateToken(auth).then(function(uuid) {
// Token Valid Code here
next();
}).catch(function(err) {
// Token failure handling code here
res.status(401).json(err);
})
}
}
now you can use this middleware two ways:
First is using middleware for the routes wherever needed.. as written below:
var session = {
v1: require('./routes/v1/session')
}
// Skipped for login
app.post('/v1/session/login', session.v1.login);
app.get('/v1/session/logout/:uuid', middleware.authTokenValidator, session.v1.logout);
var modify = {
v1: require('./routes/v1/modify')
}
app.put('/v1/modify/:uuid/password', middleware.authTokenValidator, modify.v1.password);
app.put('/v1/modify/:uuid/mobile', middleware.authTokenValidator, modify.v1.mobile);
app.put('/v1/modify/:uuid/email', middleware.authTokenValidator, modify.v1.email);
so basically using next function you can pass any number of functions as middleware and call next middleware function.
The second way is passing middleware globally and handle exceptions like login URL:
define all the routes above middleware which should not go through middleware and others below middleware as below code for reference:
var session = {
v1: require('./routes/v1/session')
}
// Skipped for login
app.post('/v1/session/login', session.v1.login);
// All the requests except the above path will go throgh this middleware
app.use(middleware.authTokenValidator)
app.get('/v1/session/logout/:uuid', session.v1.logout);
var modify = {
v1: require('./routes/v1/modify')
}
app.put('/v1/modify/:uuid/password', modify.v1.password);
app.put('/v1/modify/:uuid/mobile', modify.v1.mobile);
app.put('/v1/modify/:uuid/email', modify.v1.email);
and, the Third approach is similar to the Second one but checking request path in middleware and skip it, which is not preferable...
var middleware = {
authTokenValidator = function(req, res, next) {
var no_validate_path = ['/v1/session/login']
// Skipping the login path here
if (no_validate_path.indexOf(req.path) >= 0) {
next()
} else {
var auth = req.headers.authorization;
global.utils.validateToken(auth).then(function(uuid) {
// Token Valid Code here
next();
}).catch(function(err) {
// Token failure handling code here
res.status(401).json(err);
})
}
}
}
And your route code as:
//Adding middleware for all the paths
app.use(middleware.authTokenValidator)
var session = {
v1: require('./routes/v1/session')
}
app.post('/v1/session/login', session.v1.login);
app.get('/v1/session/logout/:uuid', session.v1.logout);
var modify = {
v1: require('./routes/v1/modify')
}
app.put('/v1/modify/:uuid/password', modify.v1.password);
app.put('/v1/modify/:uuid/mobile', modify.v1.mobile);
app.put('/v1/modify/:uuid/email', modify.v1.email);
References to read about middlewares https://expressjs.com/en/guide/using-middleware.html

I am wondering how to communicate between controllers

I want to invoke the user creation API after confirming the token internally in the server when I click the authentication link in the e-mail to implement the membership method using e-mail authentication.
//emailcontroller.js
router.get('/register/token', function(req, res) {
// check token
if(check(req.params.token)) {
request('http://localhost:8080/api/user', function(data) {
});
}
});
//usercontroller.js
router.post('/api/user', function(req, res) {
var user = new User();
user.userId = req.body.userId;
user.userPw = req.body.userPw;
user.save();
});
I want to invoke the user creation API after confirming the token internally in the server when I click the authentication link in email in order to implement membership method using email authentication.
As mentioned above, the email controller and the user controller are divided and each is routed. I want to modularize the code so that I want to call the existing user creation API to use it for general purpose rather than creating and exports common functions for a specific controller.
/*I do not want to implement it this way.*/
//emailController.js
router.get('/register/token', function(req, res) {
// check token
if(check(req.params.token)) {
userContoller.createUserFromEmail(userId, userPw);
}
});
//userController.js
exports.createUserFromEmail = function(userId, userPw) {
var user = new User();
user.userId = userId;
user.userPw = userPw;
user.save();
}
However, I have never seen communication between controllers in many examples. So I do not know if the way I thought was right. Rather, I think the cost of calling api internally on the server might be higher.
I want to know the correct pattern for communication between controllers. Please bear in mind that there is only a stack overflow when raising a question.
You got the right idea about exposing your API functionality as stand-alone functions (or classes). To avoid duplication, just call your internal methods from within your route handlers. So in your example:
router.post('/api/user', function(req, res) {
createUserFromEmail(req.body.userId, req.body.userPw);
});
In my own projects, I use classes to create my API. First I define a class with just the functionality and then I expose the methods in the route handlers:
export default class User {
read() {
}
create() {
}
update() {
}
delete() {
}
}
const user = new User();
router.get('/user/:id', (req, res) => user.read(req.params.id));
router.post('/user', (req, res) => user.create(req.body.data));
router.put('/user/:id', (req, res) => user.update(req.params.id, req.body.data));
router.delete('/user/:id', (req, res) => user.delete(req.params.id));
This should give you an idea of what you can do. You can write custom middleware and class decorators to reduce the boilerplate.
From your question what I understood:
You want to validate internally the token passed in query parameter, before doing anything else in the user controller.
I believe you are using express, and with express comes middlewares.
From docs:
Middleware functions are functions that have access to the request object (req), the response object (res), and the next middleware function in the application’s request-response cycle. The next middleware function is commonly denoted by a variable named next.
What I usually do and a generally good practice is, pass the token in create user api and attach to email body.
for example:
api/user?token=somerandomstringloremispum
Route file:
router.post('/user', validateEmail, userController.create);
here validateEmail is a middleware function and will be invoked before create method of userController.
Now in your validateToken method, you can simply validate your token like:
function validateEmail (req, res, next) {
if(!valid(req.query.token)) {
//return with appropriate invalid token msg using res.json() or however you like
}
// if validated call `next middleware` like so:
next();
// this will allow `create` method of userController be invoked
}

Why does req.params return an empty array?

I'm using Node.js and I want to see all of the parameters that have been posted to my script. To get to my function, in my routes/index.js I'm doing:
app.post('/v1/order', order.create);
Then in my function, I have:
exports.create = function(req, res, next) {
console.log( req.params );
But it's returning an empty array. But when I do:
exports.create = function(req, res, next) {
console.log( req.param('account_id') );
I get data. So I'm a bit confused as to what's going on here.
req.params
can only get the param of request url in this pattern:/user/:name
req.query
get query params(name) like /user?name=123 or body params.
req.params only contain the route params, not query string params (from GET) and not body params (from POST). The param() function however checks all three, see:
http://expressjs.com/4x/api.html#req.params
I had a similar problem and thought I'd post the solution to that for those coming here for the same reason. My req.params was coming out as an empty object because I declared the URL variable in the parent route. The solution is to add this option to the router:
const router = express.Router({ mergeParams: true });
With postman, you can have two types of get requests:
Using x-www-form-urlencoded and passing data through the body.
Using url parameters
You can always use this code snippet to always capture the data, no matter how you pass it.
/*
* Email can be passed both inside a body of a json, or as
a parameter inside the url.
* { email: 'test#gmail.com' } -> test#gmail.com
* http://localhost/buyer/get/?email=test#gmail.com -> test#gmail.com
*/
let { email }: { email?: string } = req.query;
if (!email) email = req.body.email;
console.log(email);
Add this for your server.js or app.js:
app.use(express.urlencoded({ extended: true })) // for parsing application/x-www-form-urlencoded

Resources