In a node application with ExpressJS we have CRSF middleware enabled.
This works great, however we have some routes starting with /api and accepting POST request which fail (forbidden) because there is no CRSF token of course.
How can we bypass/avoid CRSF for /api posts?
You can conditionally pass inside of middleware, so one option is to look to a pattern like this:
function yourMiddleware(req, res, next) {
if ( null !== req.path.match(/^\/api/) ) {
next();
}
//your CRSF behavior here
}
What about registering those routes before the CSRF middleware? Like:
var express = require('express');
var app = express();
app.use(express.bodyParser());
app.use(express.cookieParser('your-secret'));
app.use(express.session());
app.use('/api', require('path to your module that does not need csrf'));
app.use(express.csrf());
app.use('/othermount', require('path to your module that needs csrf'));
Edit: Expanded code example to clarify what I was thinking.
Related
I was learning Express in Node.js and came across Router() allowing us to modularize our routes. But than I found this code:
// we'll create our routes here
// get an instance of router
var router = express.Router();
...
// route with parameters (http://localhost:8080/hello/:name)
router.get('/hello/:name', function(req, res) {
res.send('hello ' + req.params.name + '!');
});
// apply the routes to our application
app.use('/', router);
What confused me is that why we need to use app.use('/', router); to apply the routes. That is, what if we use app.get('/', router);
I am giving you a simple code example to make you understand the use of express.Router(). Yes you are right that it helps in modularization. Actually it makes our main app.js file clean from all the routes. We just put all those routes based on their purposes in different files and require them when needed.
so suppose I have two files app.js and register.js
// app.js file's code goes here
let express = require("express")
let app = express()
let register = require("./routes/register")
app.use(register) // This will tell your app to import all those routes which are in register
// register.js file's code goes here
let express = require("express")
let router = express.Router()
router.get("/register", callback_function);
router.post("/register", callback_function);
module.exports = router;
So basically what I am trying to show is your register.js can contain all types of HTTP requests(GET, POST, PUT,...) and when we use app.use(register) this will handle all those routes. app.get("route_path", callback_function) is only for handling get requests to that path.
Router is just a middleware of it's own. If you use app.get('/', router) you will use the router just for GET requests. Using use channels all requests there.
When app.use is used then it handled all the HTTP methods, but when app.get is used it takes just GET method.
Added advantage to app.use is that route will match any path that follows its path immediately with a /.
For example:
app.use('/v1', ...)
will match /users, /users/accounts, /users/accounts/account-id, and so on.
I was trying to comprehend when do we need to use app.use in our node Express
While searching on web, I stumbled on this answer on reddit stating the difference between app.get and app.use
Based on which, I was able to summarise the following things.
app.use act as a super route or middleware? meaning that it gets called on every route written below/after app.use?
Also, would appreciate if someone could add more information/practise about app.use.
When using ExpressJS with NodeJS you can use app.get and app.use for several useful aspects.
After initializing your App like let app = express();, you can find below some examples:
app.use(...)
As you correctly pointed, it is useful for "middlewares", it will apply to all the GETs, POSTs, etc. you indicate afterwords. For example, you can use a Middleware only before the GETs you want to be "with user/pass authentication".
Indicate the folder for static contents: app.use(express.static(__dirname + "/public"));
Including a parser for JSON contents: app.use(bodyParser.json());
Define the "Cookie Parser" signing string: app.use(cookieParser("Signing text example"));
Separate Routers for your URLs in different files: app.use("/api", apiRouter); or app.use("/news", newsRouter); or app.use("/", siteRouter);
For a custom error handler: app.use(sites404handler); or app.use(globalErrorHandler);
app.get(...)
When talking about app.get(...) you are indicating which URLs will be visited via a GET method. But you can use several options here:
Indicate you have a home page: app.get("/", function(req, res) { res.send("Hello world!"); });
Accept POST requests: app.post("/", function(req, res) { res.send("Hello world! With POST call."); });
You can also separate it in another file as "apiRouter.js" and include there: let router = express.Router(); router.route("/books").get(function(req, res) { res.send("/api/books/ called via a Router"); });
app.set(...)
Remember that you also have the option app.set(...). This is useful for example to define View Engines like Handlebars (.hbs files).
Hope this can help!
Use for static path
//Set static path
app.use(express.static(__dirname + '/public'));
use as router
//user
app.use('/', require('./controllers/user'));
use for handline middleware
//Body-parser
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({
extended: true
}));// Body parser use JSON data
Use for custom middleware
// force https
app.use ( (req, res, next) =>{
if (req.secure) {
// request was via https, so do no special handling
next();
} else {
// request was via http, so redirect to https
res.redirect('https://' + req.headers.host + req.url);
}
});
app.get route handler is applied to GET requests, either for specified paths or all paths:
Routes HTTP GET requests to the specified path with the specified callback functions.
app.use middleware is applied to all requests, either for specified paths or all paths:
Mounts the specified middleware function or functions at the specified path: the middleware function is executed when the base of the requested path matches path.
use is used to apply some logic (middleware) to specific route or entire application, regardless of request method.
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
I wanted to create MVC based architecture on the express for that I want to overwrite the Express Router.
Consider following as an example:
// usual express router
var userRouter = require('express').Router();
userRouter.get("/",function(req, res, next){
//.....
});
userRouter.post("/",function(req, res, next){
//.....
});
app.use('/users', userRouter);
//routes.js
module.exports = function(app){
var userRouter = app.Router();
userRouter.get('/', {controller: 'User', action: 'show'});
userRouter.post('/', {controller: 'User', action: 'new'});
app.use('/users', userRouter);
};
So, do I have to implement router again like the express with my controller parsing functionality?
Rewriting already existing functionality is commonly accepted as bad practice. Express has the option to modify data via middleware services.
If neither the native router, nor the middleware functionality works for you, then maybe you should go and change the framework to Hapi.
I judge my own short experience, Hapi provides better accommodation for routing and server magic.
I have a project based on express with a required authentication based on passport.
The backoffice is an angularjs app served as static files.
My authentication code is completly based on https://github.com/jaredhanson/passport-local/blob/master/examples/express3-no-connect-flash/app.js
To do not serve the angular app if you are not authenticated. I have try by adding ensureAuthenticated on the /admin route but it make the route not working (404). Once I remove ensureAuthenticated the /admin is served.
app.use(express.static(path.join(__dirname, 'public')));
app.use('/admin', ensureAuthenticated, express.static(path.join(__dirname, 'admin')));
//serve routes
app.use(app.router);
The public folder contains the login page.
How could I achieve this ?
Ran into same issue, this is what I ended up doing!
app.use doesn't let you chain middlewares in that way. The various
app.VERB functions do, but app.use doesn't. That's for one middleware
at a time.
If you split the 2 middlewares out into separate calls, you should get
the results you want:
app.use('/admin', ensureAuthenticated);
app.use('/admin', express.static(path.join(__dirname, 'admin')));
Cannot use basic authentication while serving static files using express
You can check the route using middleware and redirect them if they aren't logged in and are hitting admin pages, something like (untested):
app.use(function(req, res, next) {
if (req.user == null && req.path.indexOf('/admin') === 0)
{
res.redirect('/login');
}
next();
});
app.use('/admin', function(req,res,next){
if(req.user){
return express.static(path.join(__dirname, 'public'));
} else {
res.render(403, 'login', {message:'Please, login!'});
}
});
//serve routes
app.use(app.router);
Update for express#4.16.4+, passport-jtw#0.4.0, and passport-jwt#4.0.0
First setup a passport auth strategy. If you use a jwt, you can take a token from a query parameter, if not you can use another Extract function (or multiple using Jwt.ExtractJwt.fromExtractors())
passport.use('basic-user',
new Jwt.Strategy({
...jwtConfig.options,
jwtFromRequest: Jwt.ExtractJwt.fromUrlQueryParameter('token')
}, verifyUser)
);
Then you can use a passport authenticate function before serving static files
app.use('/files', [
passport.authenticate(['basic-user'], { session: false }),
express.static(path.join(__dirname, 'files')) //make sure you access proper directory
])
You could also chain middlewares as an array to achieve this goal:
app.use('/admin', [ensureAuthenticated, express.static(path.join(__dirname, 'admin'))]);
The accepted answer felt a bit partial (and may not work in some cases), so here's a bit more verbose and generalized answer:
// Here we'll attach the user object. The correct ordering
// of these middleware functions is important.
app.use(
'/some-restricted-static-path',
passport.authenticate("bearer", { session: false })
);
// As the user object should now be attached (if authorized), we can now
// verify it to be so.
app.use('/some-restricted-static-path', (req, res, next) => {
if (!!req.user) {
// The user exists, we can continue.
// Here you can also validate the role etc if necessary.
next();
} else {
// No user object found, terminate the pipeline with .end().
res.status(401).end();
}
});
// And finally, here's the actual handler that won't be accessed if
// something went wrong earlier.
app.use(
'/some-restricted-static-path',
express.static(
path.join(
__dirname,
"../dist/attachments"
)
)
);
Explanation: In Express, the middleware is processed one-by-one. If one of the middleware terminate the process, every middleware after it will be skipped. So, knowing this, we can first attach the user object, then validate it, and finally either grant or deny access.