control order of express/connect middleware - node.js

I'm trying to add authentication middleware that should prevent access to part of the site:
app = express()
.get('/api/test', function (req, res) { ... })
.use('/api', function (req, res, next) {
if (req.param('key')) {
next();
} else {
res.json(401, {
message : 'Authentication failed'
});
res.end();
}
})
.get('/api/data', function (req, res) { ... });
And my expectation that calls to the /api/data will be first processed by the key checker and then (if it is successful) by the /api/data handler. But instead the request processed by the '/api/data' first.
It seems that the checker works for the /api/something_that_does_not_exist, but not for /api/something_that_exist.
Maybe I missed something in express/connect documentation?
Update I've tracked this up to the fact that the first get/post call initializes the router middleware so it is executed first.

Once you declare a route, Express inserts the router middleware into the middleware stack at that point in setting up the app.
In your case, because you insert .get('/api/test', ...) before you insert your key checking middleware, the router middleware gets inserted and will take precedence (also for the /api/data route you declare later) and your key checker is never called.
Here are two solutions:
// separate middleware, used for all routes that need checking
var keyChecker = function(req, res, next) {
...
};
app.get('/api/test', function(req, res) { ... });
app.get('/api/data', keyChecker, function(req, res) { ... });
// or, as an alternative, create a 'catch-all' route between the routes that don't
// need to be checked, and the ones that should; this will also match non-existing
// routes (like '/api/foobar'), which might or might not be an issue;
app.get('/api/test', function(req, res) { ... });
app.all('/api/*', function(req, res, next) { // 'all' means 'all methods'
// keychecker code
});
app.get('/api/data', function(req, res) { ... });
A third solution could be to explicitly check for /api/test in the key checking middleware itself (req.path === '/api/test'), and just call next() if it matches.

Related

setting up a middleware in router.route() in nodejs (express)

what I want it to do.
router.post('/xxxx', authorize , xxxx);
function authorize(req, res, next)
{
if(xxx)
res.send(500);
else
next();
}
I want to check for session in each route.
But since the routers are written in this way.
router.route('/xxx/xxxx').post(function(req, res) {
// blah lah here...
//
});
So how can I set up a middleware that will check for session and I wanted to make things a bit more generic and wanted to have a single authorize function doing a single thing instead of checking in every request.Any suggestions.
Define a middlware function before you define / include your routes, this will avoid you checking for a valid session in every route. See code below for an example on how to do this.
If some routes are public, i.e. they do not require a user to have a valid session then define these BEFORE you 'use' your middlware function
var app = require("express")();
//This is the middleware function which will be called before any routes get hit which are defined after this point, i.e. in your index.js
app.use(function (req, res, next) {
var authorised = false;
//Here you would check for the user being authenticated
//Unsure how you're actually checking this, so some psuedo code below
if (authorised) {
//Stop the user progressing any further
return res.status(403).send("Unauthorised!");
}
else {
//Carry on with the request chain
next();
}
});
//Define/include your controllers
As per your comment, you have two choices with regards to having this middleware affect only some routes, see two examples below.
Option 1 - Declare your specific routes before the middleware.
app.post("/auth/signup", function (req, res, next) { ... });
app.post("/auth/forgotpassword", function (req, res, next) { ... });
//Any routes defined above this point will not have the middleware executed before they are hit.
app.use(function (req, res, next) {
//Check for session (See the middlware function above)
next();
});
//Any routes defined after this point will have the middlware executed before they get hit
//The middlware function will get hit before this is executed
app.get("/someauthorisedrouter", function (req, res, next) { ... });
Option 2 Define your middlware function somewhere and require it where needed
/middleware.js
module.exports = function (req, res, next) {
//Do your session checking...
next();
};
Now you can require it wherever you want it.
/index.js
var session_check = require("./middleware"),
router = require("express").Router();
//No need to include the middlware on this function
router.post("/signup", function (req, res, next) {...});
//The session middleware will be invoked before the route logic is executed..
router.get("/someprivatecontent", session_check, function (req, res, next) { ... });
module.exports = router;
Hope that gives you a general idea of how you can achieve this feature.
Express routers have a neat use() function that lets you define middleware for all routes. router.use('/xxxxx', authorize); router.post('/xxxx', 'xxxx'); should work.
Middleware:
sampleMiddleware.js
export const verifyUser = (req, res, next) => {
console.log('Verified')
next();
}
Routes
import express from 'express';
import { verifyUser } from './sampleMiddleware.js';
const userRoutes = express.Router();
userRoutes.route('/update').put(verifyUser, async function(){
//write your function heere
});
You've probably gotten the answer you need but I'll still drop this
router.route('/xxx/xxxx').get(authorize, function(req, res) {...});

expressjs route: specific route is not recognized

folks.
I am doing just a very simple API (localhost development), and I am curious why the last route is never got by ExpressJS server.
Here are the routes:
var express = require('express');
var bodyParser = require('body-parser');
var router = express();
// (...) other variables defined here...
router.all('*', function(req, res, next) {
res.header('Access-Control-Allow-Origin', 'localhost');
res.header('Access-Control-Allow-Methods', 'PUT, GET, POST, DELETE, OPTIONS');
res.header('Access-Control-Allow-Headers', 'Content-Type');
next();
});
// Works great!
router.get('/', function(req, res) {
res.render('index');
});
// Works great!
router.get('/api/states', function(req, res) {
res.json(states);
});
// Works great!
router.get('/api/cities/:state', function(req, res, state) {
res.json(cities);
});
// Never Works!!
router.get('/api/stores/:state/:city', function(req, res, state, city) {
res.json('{"result": true}');
});
router.use(express.static(__dirname + '/../../www'));
router.set('views', __dirname + '/../../www');
router.engine('html', require('ejs').renderFile);
router.set('view engine', 'html');
router.listen(process.env.PORT || 9000);
From my app I call these URLs to the last route:
http://localhost/api/stores/sp/city_name
It is always 404.
I simply can not realise why this is 404. The last route, witch never worked, is basically a copy from the previous route (that works great).
Could someone point what I am doing wrong here?
ps: the last route should accept a third optional parameter. I also had tried this:
// Never Works!!
router.get('/api/stores/:state/:city/*?', function(req, res, state, city) {
res.json('{"result": true}');
});
But, if I can not do it work with two defined parameters, it will not work with extra optional parameters, right?
Any advice is very appreciate.
Thank you.
You have too many arguments in your handler--function handlers that take four arguments are reserved for error handling, i.e. only called when somewhere in the middleware chain next(error) was called, or a exception was caught. Read the docs for further info.
// Remove `state` and `city` arguments, access via `req.params.*`
router.get('/api/stores/:state/:city', function(req, res) {
var state = req.params.state
var city = req.params.city
res.json('{"result": true}');
});
The reason you are not getting a 404 for the other handler is because you're accepting three arguments. So I should point out the state argument will not be what you expect, it will be a function to invoke the next middleware, commonly named next.
// NOPE! function (req, res, state)
router.get('/api/cities/:state', function(req, res) {
var state = req.params.state
res.json(cities);
});
// Common middleware handler, third parameter is a function that when invoked will call the next middleware
router.get('/some/uri', function (req, res, next) {
doSomethingAsync()
.then(data => {
req.data = data
next()
})
.catch(err => next(err))
})

NodeJS Express: How to interrupt the routing from outside the middleware/router?

I implemented a very simple middleware to check the permissions for the user:
app.js
...
var security = require('./lib/security');
app.use(security.init);
...
lib/security.js
var session;
var request;
var response;
function init(req, res, next) {
request = req;
response = res;
session = req.session;
next();
}
function adminRequired(){
if (!isAdmin()){
response.redirect('/login');
response.end();
return true;
}
return false;
}
...
The best way I found to interrupt the flow is the following:
routes/mycontroller.js
router.get('/', function(req, res, next) {
if(security.adminRequiredHtml()){return;} // now it actually interrupt the execution
res.render('admin',{});
res.end();
});
However, I would like to use it like this:
routes/mycontroller.js
router.get('/', function(req, res, next) {
security.adminRequiredHtml(); // <- interrupt the request
res.render('admin',{});
res.end();
});
It correctly perform the redirect, but the execution continues :(
I've tried a few solutions like but it doesn't really work:
response.end() -> close the output but continues the execution
process.end() -> it's too radical, terminates the execution but it also kill the server :(
I've been thinking about using a throw but I don't know where to catch it and make it terminate gracefully (no stacktrace)
You could create a custom Router that is secured and add your secure Routes to that:
var secureRouter = express.Router();
// every request on this router goes throug this
secureRouter.use('*', function (req, res, next) {
if(isAdmin()) next();
// if you don't call next() you interrupt the request automaticly
res.end();
});
// protected routes
secureRouter.get('/user', function(req, res){/* whatever */});
secureRouter.post('/user', function(req, res){/* whatever */});
app.use(secureRouter);
// not protected
app.get('/api', function(req, res){/* whatever */});
Express doc for using middlewares
You're actually looking for middleware, I think.
function myMiddleware (req, req, next) {
if (!isAdmin()) {
res.redirect('/login');
res.end();
} else {
//Proceed!
next()
}
}
router.get('/', myMiddleware, function(req, res, next) {
res.render('admin',{});
res.end();
});
You can chain as many of those as you'd like to handle whatever logic you need. Just make sure you call next() if you're supposed to move on!

Express - public directory divided for authorized/unauthorized users

I have an app written in express.js and I'm trying to divide this application to 2 sections:
one for unauthorized users (with routes only to / - landing page, /login and /* - error404)
and second (routes will be: / - landing page, /app/* - angular SPA which will handle routing on its own)
Express is also configured to take static files from /unauth/public/
And I want to add second static folder for request from authorized routes - /auth/public
which goes to /app/*
My route config looks like this:
var authRoutes = express.Router();
var unauthRoutes = express.Router();
authRoutes.get('/app/*', function(req, res, next) {
if(!req.isAuthenticated())
return res.redirect("/login/");
res.send("AUTHORIZED");
});
unauthRoutes.get('/', function(req, res, next) {
res.send("LANDING PAGE");
});
unauthRoutes.get('/login/', function(req, res, next) {
if(req.isAuthenticated())
return res.redirect("/app/");
res.send("LOGIN PAGE");
});
unauthRoutes.get('/registration/', function(req, res, next) {
if(req.isAuthenticated())
return res.redirect("/app/");
res.send("REGISTRATION PAGE");
});
unauthRoutes.get('/*', function(req, res, next) {
res.send("ERROR 404");
});
app.use('/', authRoutes);
app.use('/', unauthRoutes);
I tried to modify req.url and call another static oruter express.static('auth/public') based on this:
Using express.static middleware in an authorized route
But I don't know, how to handle route app.get('/auth/*', ...) - previous modification will replace url and this route will never be called..
You could try something like this:
// Create your static middlewares
var unauthStatic = express.static('unauth/public');
var authStatic = express.static('auth/public');
// This goes in place of where you would normally load your static middleware
app.use(function(req, res, next) {
if (req.isAuthenticated()) {
authStatic(req, res, next);
} else {
unauthStatic(req, res, next);
}
});
edit:
if you want authenticated users to be able to access files from both the auth and unauth directories, you can make two calls to app.use, like this:
app.use(unauthStatic);
app.use(function(req, res, next) {
if (! req.isAuthenticated()) {
return next();
}
authStatic(req, res, next);
});
Remember that express uses middleware in a stack, meaning to serve a given request, all registered middleware is used in the order it's used. Once a bit of middleware calls req.send, no further middleware gets executed. Anyway, try something like this:
function Authorization(req, res, next) {
if(!req.isAuthenticated())
return res.redirect("/login");
next();
}
var AnonRouter = express.Router()
// GET /style.css will request /unauth/public/style.css
.use(express.static('unauth/public'))
.get('/', function (req, res) { })
.get('/login', function (req, res) { });
var AuthRouter = express.Router()
.use(Authorization)
// GET /app/style.css will request /auth/public/style.css
.use(express.static('auth/public'))
.get('*', function (req, res, next) {
// Handle reqs for non-static files
});
app.use('/', AnonRouter);
app.use('/app', AuthRouter);
app.get('*', function (req, res) {
res.status(404).send('404!');
});
But I don't know, how to handle route app.get('/auth/*', ...) - previous modification will replace url and this route will never be called..
This statement makes me think that you are trying to somehow handle the request after express's static middleware has been called. This is not possible: the static middleware serves static files and you cannot execute additional logic after it does so, but you can run stuff before! Note that in my code, the Authorization middleware will run before the static file is sent.

Adding middleware after app.router

I have a server with the following defined :
app.get('/', function(req, res) {
// gets something
}
app.post('/', function(req, res) {
// updates something, need to be authenticated
}
Now I want the post action to be only for authenticated users, so I want to add a auth middleware between them like this :
app.get('/', function(req, res) {
// gets something
}
app.use('/', function(req, res) {
// check for authentication
}
app.post('/', function(req, res) {
// updates something, need to be authenticated
}
This way, GET gets through and for POST, user has to be authenticated.
The problem is that express doesn't go in to my app.use middleware. If i put the app.use middleware before all of the app.VERB routes, it works.
Is there any way to do it like I want ?
When you declare your first route, Express automatically inserts app.router into the middleware chain. Since the router gets to handle any following routes, any middleware that you declare after that first route will not get to handle your routes.
But instead of using app.use, you can use the fact that route-handlers are very similar to middleware:
app.get('/', function(req, res) {
// gets something
});
app.all('/', function(req, res, next) { // catches GET, POST, ... to '/'
// check for authentication
});
app.post('/', function(req, res) {
// updates something, need to be authenticated
});
But if you only have a single route that needs to be passed through the middleware, it makes sense to follow the advise of #hiattp and add the middleware to the route declaration immediately.
I like to put this type of check in a reuseable method and pass it into the route handler:
function ensureAuth(req, res, next){
if(req.user) next(); // Auth check
else res.redirect('/');
}
app.post('/', ensureAuth, function(req,res){
// User is authenticated
}

Resources