I'm using the standard express CSRF module. It seems to work correctly, except that after logging out and logging in again, it's still possible to use a CSRF token generated in the previous session. I thought it would be enough to invalidate the session - see example of code here, but that's not working:
const csrf = require("csurf");
const csrfProtection = csrf({ cookie: true });
//CSRF protected route
app.post("/editUser",csrfProtection,async function (req, res, next) {
//Do stuff
});
//Logout route
app.get("/logout", function (req, res) {
req.session.destroy();
});
What do I need to do to make sure CSRF tokens are invalidated on the user logging out?
Not sure if this is the best solution, but using the clearCookie function on both log out and log in routes seems to do the trick, e.g.
app.get("/logout", function (req, res) {
res.clearCookie("_csrf");
req.session.destroy();
});
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'm working an API endpoint with expressjs and protecting it with passportjs (using jwt strategy)
I have
passport.use(new JwtStrategy({...})) // Register stratrgy
router.post('/login', (req, res) => {} ) // Generates jwt token
app.use('/api', passport.authenticate('jwt', { session: false }));
app.user('/api', router) // Register the route to /api
If I put /login out of /api, this works just fine. But I want to have some resource like /login in the same root path (/api) to be not protected.
I can go ahead and add passport.authenticate('jwt', { session: false }) to every route registration except the unprotected once, but I don't like that.
I can also create a middleware that call passport.authenticate manually and check other things, but I would like to avoid re-inventing the wheel if it is already done.
Have you tried creating a route like:
app.post('/api/login')
And then after that you define your router like:
app.user('/api', router) // Register the route to /api
Finally inside your router file, at the top, you do
router.use(passport.authenticate('jwt', { session: false }))
What is the differences betwenn the 3 functions use/get/post with express?
In which case is better to use express.use instead of express.get/post?
app.use is used to load the middleware functions.
app.use example:
var myUseFunction = function (req, res, next) {
console.log('Hello World!');
next();
}
app.use(myUseFunction);
It does not have limitations for any restful api http verbs like POST, GET, PUT, PATCH, and DELETE.
app.get is route method is derived from one of the HTTP methods, and is attached to an instance of the express class.It serves the pupose of get request of apis.
GET method route
app.get('/', function (req, res) {
res.send('GET request to the page');
});
app.post is route method is derived from of the HTTP methods, and is attached to an instance of the express class. It serves the pupose of post request of apis.
POST method route
app.post('/', function (req, res) {
res.send('POST request to the page');
});
use is for middleware, e.g., all requests. It says it right in the docs:
Mounts the specified middleware function or functions at the specified path.
get is... for GET requests. post is for POST requests.
I'm using csurf in my express project. I have 3 files:
app.js - Main entry point
routes/index.js - Index routes
routes/users.js - User routes
It's standard boilerplate when using express application generator.
I have a route in index.js:
router.get('/', csrfProtection, function(req, res, next) {
res.render('index', {
csrfToken: req.csrfToken()
});
});
The page for this route contains a form that has a hidden field with csrf token:
input(name='_csrf', type='hidden', value='#{csrfToken}')
All works fine and I can see the csrf token in the source.
When the form is submitted it's handled buy the route in routes/users.js:
router.post('/login', csrfProtection, function(req, resp) {
if(!validator.isAlphanumeric(req.username))
console.log('Not alphanumeric');
...
});
It appears the problem is something to do with both files having to create new instances of csrf, and csrfToken. At the head of both route files I require them like so:
var csrf = require('csurf');
var csrfProtection = csrf({ cookie: true });
If I put the login route into routes/index.js it works fine, which has made me think maybe both instances are using different csrf tokens.
Any ideas?
Yes, I believe it is using different CSRF tokens. I got around the issue by defining an init function in my sub-modules, and then passing the CSRF token into that. That way the CSRF token is only created once. I think creating the CSRF token in app.js is probably best, and then you can pass it into your various sub-modules.
Ex:
in users.js:
function init(router, csrfProtection) {
router.post('/login', csrfProtection, function(req, resp) {
if(!validator.isAlphanumeric(req.username))
console.log('Not alphanumeric');
...
});
}
module.exports.init = init;
in app.js:
...initialize router and CSRF protection...
var users = require('./users.js');
users.init(router, csrfProtection);