I'm using a library called Grant in my application. Previously I have included it following the examples in the readme linked above, however I now have the need to conditionally include it and I can't seem to make it work.
How it worked before
const grant = new Grant(grantConfig);
app.use(grant);
I've done conditional middleware in Express before, so I thought including Grant wouldn't be a problem.
How I Tried (doesn't work)
const grant = new Grant(grantConfig);
app.use((req, res, next) => {
if (someBooleanVariable) {
next();
} else {
grant(req, res, next);
}
});
So this doesn't work. I think it may have something to do with Grant being an instance of Express versus just a regular middleware function, but I'm not sure. You can see how Grant is implemented here. The Express docs say that it should be treated the same, but I might also be misunderstanding them.
Note: I need this to work on per-request basis, hence the middleware style approach.
If you want to use a particular middleware on only a subset of routes:
// On a single route:
app.get('/some/route', grant, function(req, res) {
...
});
// On a set of routes with a particular prefix:
app.use('/api', grant, apiRouter);
// On a specific router:
let apiRouter = express.Router();
apiRouter.use(grant);
apiRouter.get(...);
Related
I have a node.js application where I'm using multiple app.use statements. I want to include the app.use(helmet.frameguard({ action: 'deny' })); line to prevent clickjacking by preventing my site from appearing in iframes and I wanted to ask whether it matters where I place this line in the order of the app.use statements? Do I need to place it in a particular place amongst all the other app.use statements (e.g. app.use(cookie-parser());)
I mean it does, all middleware run line by line or you can say synchronously.
Just make sure to you should place it above route initialization (since if it goes to routes it might be possible that you are returning the function response from there only, so it wont go to next middleware, since response already sent). Can paste it above or below cookie-parser.
Summary: I recommend putting helmet() first.
Helmet maintainer here. This is more of an Express question than a Helmet question.
Express apps run their middleware in sequence. For example, the following will print foo, then bar, for every request:
function fooMiddleware(req, res, next) {
console.log("foo");
next();
}
function barMiddleware(req, res, next) {
console.log("bar");
next();
}
app.use(fooMiddleware);
app.use(barMiddleware);
// ...
Alternatively, this will print bar, then foo:
app.use(barMiddleware);
app.use(fooMiddleware);
So if you want Helmet's headers to be applied to all responses, it's important to put them first.
For example, this will set the Helmet headers for every request:
const app = express();
app.use(helmet());
// ...
But this one might not, because the static file middleware might get to it first:
const app = express();
app.use(express.static("./static_files"));
app.use(helmet());
// ...
It's worth noting that route handlers (like app.get('/foo')) are conceptually very similar to middleware. These two code snippets are nearly the same (though you should use the former in a real application):
app.get("/foo", (req, res) => {
res.send("Hello world!");
});
app.use((req, res, next) => {
if (req.method === "GET" && req.url === "/foo") {
res.send("Hello world!");
} else {
next();
}
});
I made a custom middleware for Express router that allows me to whitelist certain endpoints of my API to be excluded from authentication. However I have a route where I depend on URL parameter and I can't get my middleware to work as intended with it. Apparently :profileId doesn't do anything and my API endpoint still requires authentication.
The reason I need that path to be excluded from authentication is because of my React frontend that should display that data to the public (without people registering and logging in). Any tips how to solve this?
const apiAuth = (req, res, next) => {
let authRequired = true;
if (
req.path == "/api/users/register" ||
req.path == "/api/users/login" ||
req.path == "/api/profiles/:profileId"
) {
authRequired = false;
}
if (authRequired == true) {
// Auth check logic
}
}
There's a few better approaches for handling the requirement of middleware, that are generally used over the method you're suggesting:
Only include your authentication middleware on routes you require it:
const authenticationMiddleware = (req, res, next) => {
// your login check logic
}
router.get('/api/users/me', authenticationMiddleware, (req, res, next) => {
// your route logic, this endpoint now requires you to be logged in, as you have specified your authentication middleware in the declaration,
})
router.get('/api/profiles/:profileId', (req, res, next) => {
// your route logic, this endpoint does not require you to be logged in as you have not put the middleware in the route delcaration
})
Or, add the authentication middleware based on where your routes are called:
router.get('/api/profiles/:profileId', (req, res, next) => {
// your route logic, this endpoint does not require you to be logged as we have not told our router to use the middleware yet
})
router.use(authenticationMiddleware)
router.get('/api/users/me', (req, res, next) => {
// your route logic, this endpoint now requires you to be logged in, as the router has been told to use the middleware at this point.
})
Why these methods? Try and think of all the router or app calls you're making as adding to a stack which express uses to handle calls to your site or API. As it works its way through looks for routes it will call any middlewares it finds on its way.
This solves the issue of having to declare a list or array of routes which do or don't require a particular piece of authentication, etc.
You'll also need to make sure to call next() in your middleware if you want it to work, as this tells express to continue going through all the routes/middleware's it has.
From what I have read here and here, the order in which you place your middleware function matters, as you can have certain routes not go through the middleware function if it is placed before the route, and the routes which are placed after will go through this middleware function.
I am seeing mixed results as my dev environment is not respecting this and my prod environment is. The code is exactly the same.
What I am trying to do is have my login route not be protected by a token checker middleware function and have the rest of my routes protected by a token.
Here is my code:
routes.get('/login', function(req, res) {
// login user, get token
});
routes.use(function(req, res, next) {
// check header or url parameters or post parameters for token
var token = req.headers['access-token'];
// decode token
if (token) {
// validate token
}
else if (req.method === 'OPTIONS') {
next();
}
else {
// if there is no token
// return an error
return res.status(403).send({
success: false,
message: 'No token provided.'
});
}
});
routes.get('/query/:keywords', function(req, res) {
console.log(req.params.keywords);
// execute query
});
app.use('/', routes);
the /query route is the only one that should have to go through the token middleware function correct? Right now I am getting the /login route also going through the token middleware function, which doesn't make sense as I shouldn't need to have a token to login.
Better yet, if there is a way to target which routes I want protected and which routes I do not want protected, this seems better than having to rely on an "order" of where the middleware function is placed.
First, follow along this usage in ExpressJS:
More than one callback function can handle a route (make sure you specify the next object). For example:
app.get('/example/b', function (req, res, next) {
console.log('the response will be sent by the next function ...')
next()
}, function (req, res) {
res.send('Hello from B!')
})
You'll notice it's definition is close to what you're declaring on routes.use(yourFunction(...)). However, there's no real reason to do it this way other than following examples you've seen in documentation, which is a good way to start nevertheless.
However, it's a flimsy implementation, express will allow hierarchies within it's .get() .post() methods, that's correct, but this is a use case specific and not what you're looking for.
What you need is to implement your custom auth process using the double callback configuration. do this:
// You can save this function in a separate file and import it with require() if you want
const tokenCheck = function(req, res, next) {
// check header or url parameters or post parameters for token
var token = req.headers['access-token'];
// decode token
if (token) {
// validate token
}
else if (req.method === 'OPTIONS') {
next();
}
else {
// if there is no token
// return an error
return res.status(403).send({
success: false,
message: 'No token provided.'
});
}
});
routes.get('/login', function(req, res) {
// login user, get token [Unprotected]
});
routes.get('/query/:keywords', tokenCheck, function(req, res) {
console.log(req.params.keywords);
// execute query [Protected with tokenCheck]
});
app.use('/', routes);
You might need to play around with the code above, but it'll guide you on the right direction, this way, you can specify particular routes to execute the tokenCheck(req, res, next) function as you want.
The easiest way to do this is to use Router Middleware to scope Routes that require Authentication and the routes that don't. Since all Routers are Middleware, we can implement them just like any other middleware. Ensuring that we place the Routers and Routes in the order that we would like our Routes to be evaluated.
In the below example, the Express server has 2 routers, a LoginRouter and an ApiRouter.
LoginRouter - Generates a Token when receiving a request to POST /login and returns that to the requester for subsequent use in the /api routes.
ApiRouter - Wraps all other routers, centralizes middleware that needs to be globally applied to all routes under /api. Is only accessible to Authenticated Requests.
The API Router is only accessible if there is a token included in the Header and that token is obtained from the LoginRouter. LoginRouter has no authentication required.
With this setup, you'll keep adding routers after the Authorization Middleware to the API Router via .use() on the ApiRouter.
The below pattern of composing Routers from other Routers is very powerful, scalable and easy to maintain.
server.js
const express = require('express')
const bodyParser = require('bodyParser')
const ApiRouter = require('./routes/api')
const LoginRouter = require('./routes/login')
const port = process.env.PORT || 1337
const server = express()
server.use(bodyParser.json())
server.use('/login', LoginRouter)
server.use('/api', ApiRouter)
server.listen(port, () => console.log(`Listening on ${port}`))
LoginRouter - /routes/login.js
const router = require('express').Router()
router.post('/', (req, res) => {
// Validate Credentials
// some validation code...
// Then create the token for use later in our API
let token = '...'
// Response 200 OK with the token in the message body
return res.status(200).send({token})
})
module.exports = router
ApiRouter - /routes/api/index.js
const router = require('express').Router()
const UsersRouter = require('./routes/api/users')
router.use((req, res, next) => {
let authorizationHeader = req.headers['authorization'] || req.headers['Authorization'] // handle lowercase
let [, token] = authorizationHeader.split(' ')
if (!token) {
return res.sendStatus(403) // Forbidden, you're not logged in
} else {
// validate the token
if (!tokenIsValid) {
return res.sendStatus(403) // Forbidden, invalid token
}
// Everything is good, continue to the next middleware
return next()
}
})
router.use('/users', UsersRouter)
module.exports = router
UsersRouter - /routes/api/users
const router = require('express').Router()
router.get('/', (req, res) => {
// We only get here if the user is logged in
return res.status(200).json({users: []})
})
module.exports = router
The application of the token middleware should not happen to the login route due to route order and the fact the login route never calls the next object. Without more information we really can't trouble shoot what is happening beyond that however you could try inspecting it in your dev environment with a debugger break and looking at the req that hits that middleware.
We can however give you some information on how to try and isolate your .use middleware and how application of middleware order applies so that you can try and separate it from the login route entirely like in the bottom of your question.
When applying middleware to only specific routes you should keep note that order and .use are for middleware that should answer the request before telling express to continue looking for other middleware that come after them in the router that will also handle the request. If you only want it on a few routes, you can add it to only a few routes by being explicit like so:
router.get('/route', [ middleware1, middleware2, ..., middlewareX])
or
router.get('/route', middleware1, middleware2, ..., middlewareX)
both patterns will work. I however find the array pattern a little more palatable since I can define a lot of middle wares I want to apply and then concatenate new middleware for specific logic, and I only need modify where I declare that concatenation to add more functionality. It'd however rare to need that many middleware and you should be able to use either.
You could also section that middleware off to a subset of routes by using a router and applying it as the first middleware to the route chain before the router.
app.use('/user', authentication, userRouter)
or you can put it inside the router as the first middleware with a .use so that it handles all requests.
So remember the general tips about middleware usage:
order matters for middleware application
optional middleware that should be applied on route basis should be applied with the other middleware in order for only that route
error handling middleware must always come last, and have four arguments (err, req, res, next)
use routers to section .use middleware to specific routes and sets of routes
You can find more information about it in the expressjs documentation for middleware
how can I add middleware to all possible routes except for these that match a given expression?
I know how to add a middleware to ones that match an expression:
app.all('/test/*', requireLogin);
but I want to require login in all routes except for a few that have a specific prefix in their paths.
If you are using express 3.x series you are out of luck here. You need to hack the middle ware to to check the the path.
app.use(function(err, req, res, next){
if(canRouteSkipLogin(req.path)
next();
else{
//Do the auth logic
}
});
canRouteSkipLogin = function(path){
//logic to find the path which can skip login
}
While in express 4.0 you can do it much easier way.
var authRoutes = express.Router();
var nonAuthRoutes = express.Router();
authRoutes.use(function(req, res, next) {
//Do Auth Logic here
});
Hope this explains.
The only way I've been able to do this is to just explicitly code for it with a guard clause in the middleware itself. So the middleware always gets called, it checks req.path against the bypass regex, and if so, just calls next() immediately and returns. This is the pattern used by things like the expressjs body-parser (via the type-is module) to no-op themselves based on checking that a given request doesn't require them to do anything.
I want to check the authorization of the users of my web app when they entered the url. But when I used an individually middleware to check the authorization, it's useless for the already existing routes, such as:
function authChecker(req, res, next) {
if (req.session.auth) {
next();
} else {
res.redirect("/auth");
}
}
app.use(authChecker);
app.get("/", routes.index);
app.get("/foo/bar", routes.foobar);
The authChecker is unabled to check the authority of the users who entered the two urls.
It only works for the unspecified urls.
And I saw a method that I can put the authChecker between the route and the route handler,
such as:
app.get("/", authChecker, routes.index);
But How can I achieve it in a simple way rather than putting the authChecker in every route?
As long as
app.use(authChecker);
is before
app.use(app.router);
it will get called for every request. However, you will get the "too many redirects" because it is being called for ALL ROUTES, including /auth. So in order to get around this, I would suggest modifying the function to something like:
function authChecker(req, res, next) {
if (req.session.auth || req.path==='/auth') {
next();
} else {
res.redirect("/auth");
}
}
This way you won't redirect for the auth url as well.
There are may ways to approach this problem but here is what works for me.
I like to create an array of middleware for protected and unprotected routes and then use when necessary.
var protected = [authChecker, fetchUserObject, ...]
var unprotected = [...]
app.get("/", unprotected, function(req, res){
// display landing page
})
app.get("/dashboard", protected, function(req, res){
// display private page (if they get this far)
})
app.get("/auth", unprotected, function(req, res){
// display login form
})
app.put("/auth", unprotected, function(req, res){
// if authentication successful redirect to dashboard
// otherwise display login form again with validation errors
})
This makes it easy to extend functionality for each middleware scopes by editing the array for each type of route. It also makes the function of each route more clear because it tells us the type of route it is.
Hope this helps.
But when I used an individually middleware to check the authorization, it's useless for the already existing routes
Express will run middleware in the order added to the stack. The router is one of these middleware functions. As long as you get your authChecker into the stack BEFORE the router, it will be used by all routes and things will work.
Most likely you have the router before authChecker because you have routes defined prior to getting your authChecker into the stack. Make sure to put all your app.use calls before any calls to app.get, app.post, etc to avoid express's infuriating implicit injection of the router into the middleware stack.