1

I have many routes. Most of them require authentication. One doesn't.

Here they are:

router.get('/secure1', function (req,res) {...})
router.get('/secure2', function (req,res) {...})
router.get('/secure3', function (req,res) {...})
router.get('/:id', function (req,res) {...})

1. Let's imagine I didn't have the public route.

At the top of the page I could just put a security check middleware, and all is well. It will only let through secure connections, and will redirect non secure.

router.use(function (req,res,next) {
   securityCheck()
   next()
})
router.get('/secure1', function (req,res) {...})
router.get('/secure2', function (req,res) {...})
router.get('/secure3', function (req,res) {...})
router.get('/:id', function (req,res) {...})

This would work. this makes all the secure routes secure, but it blocks me from the public route ('/:id').

2. I could move the public route to the top:

router.get('/:id', function (req,res) {...})
router.use(function (req,res,next) {
   securityCheck()
   next()
})
router.get('/secure1', function (req,res) {...})
router.get('/secure2', function (req,res) {...})
router.get('/secure3', function (req,res) {...})

But this way it catches all of my requests and all the secure paths are inaccessible.

3. I could put a middleware on every single secure route, but that seems a little tedious and prone to human-errors:

router.get('/secure1',securityCheck(), function (req,res) {...})

So, is there a better option I didn't consider? What is considered the best practice?

Thank you

Michael Seltenreich
  • 3,013
  • 2
  • 29
  • 55

2 Answers2

2

Out of your options I would personally prefer the first one. In the middleware you can always check on req.path or req.url to choose what to set as secure.

Another option is using HTTP authentication like in .htaccess. Have a look at https://github.com/http-auth/http-auth.

A way I have done authentication before was by passing username/password over the request body as json once and then producing a stateless Token for future requests (https://github.com/auth0/node-jsonwebtoken). In my case not many router entries needed authentication, so I handled it on the entries themselves.

Also, for extra security, use HTTPS or encode your data. Eg. How to create an HTTPS server in Node.js?

Hope it helped!

Community
  • 1
  • 1
Arthur Ceccotti
  • 380
  • 2
  • 6
  • Thanks for the answer! I agree, I feel like the first method is the easiest to maintain and human-errors would tend to make something public appear as if it's secure which is better than the opposite. With that, how do I tell the middleware to continue if anyone is requesting the last route? – Michael Seltenreich Aug 30 '16 at 06:05
  • If I were you I would distinguish the non-secure one better to start with (ie. maybe something like /public/:id) because then you won't get it confused with the secure ones (as technically the user could want to set the id parameter as the string secure3). Then at the start of the middleware you can have: if(req.path starts with public) { next() } else { ... } – Arthur Ceccotti Aug 30 '16 at 06:39
  • Of course it makes sense. And in fact I do it in other parts of my app. But, in this case the '/' route is going to a user's profile, and the '/otherThings' are routes that manipulate the profile. I don't want to make the profiles have a long address, so I am not keen to add a /public/ route in this scenario. – Michael Seltenreich Aug 30 '16 at 06:42
  • Then in that case just do: if (not secure1, 2, 3) { next()} else {...}. Or just regex it :) – Arthur Ceccotti Aug 30 '16 at 06:49
1

If /:id should match a particular pattern, say a MongoDB ObjectId, you can make the match more specific so it won't match the other routes:

router.get('/:id([a-fA-F0-9]{24})', function (req,res) {...})

If you want to match ObjectId's or nothing, you can use this:

router.get('/:id(|[a-fA-F0-9]{24})', ...);

More information here (path-to-regexp is the module that Express uses to perform URL matching).

robertklep
  • 198,204
  • 35
  • 394
  • 381
  • Wow that's amazing! I didn't know you could do that! How can I fix this (regex?) thing so it accepts MongoDB ObjectId OR nothing? Meaning '/ObjectID' and '/' will both lead there. '/EverythingElse' will lead somewhere else? – Michael Seltenreich Aug 30 '16 at 06:44
  • Thanks a lot! I upvoted your answer, I cannot select it as the correct Answer as it doesn't fully answer the question. With that, this is the most helpful thing on this page. Thank you! – Michael Seltenreich Aug 30 '16 at 06:50
  • @MichaelSeltenreich it would allow you to use solution #2 in your question. – robertklep Aug 30 '16 at 06:51
  • How do I do that? – Michael Seltenreich Aug 30 '16 at 10:13
  • 1
    Taking your second solution (with the route for `/:id` first, followed by `router.use(...)`), replace `/:id` with the regular expression, so it will only match requests for valid ObjectId's (or an empty request). Any other requests will be passed to the middleware and tested against the other routes. – robertklep Aug 30 '16 at 10:32