42

I want to verify that all our get requests have a specific token in their authentication header.

I can add this to our get endpoints:

app.get('/events/country', function(req, res) {
    if (!req.headers.authorization) {
    return res.json({ error: 'No credentials sent!' });
    }

Is there any better way to handle this in NodeJS/Express without changing every endpoint? something like a before-filter/AOP approach?

A-Sharabiani
  • 17,750
  • 17
  • 113
  • 128
kambi
  • 3,291
  • 10
  • 37
  • 58
  • You can write a simple express middleware which checks the authorization header for every HTTP request received. Make sure to app.use() the middleware before you handle any routes – amoghesturi Sep 07 '17 at 11:11

3 Answers3

95

That's what middleware is for:

app.use(function(req, res, next) {
  if (!req.headers.authorization) {
    return res.status(403).json({ error: 'No credentials sent!' });
  }
  next();
});

...all your protected routes...

Make sure that the middleware is declared before the routes to which the middleware should apply.

robertklep
  • 198,204
  • 35
  • 394
  • 381
  • 11
    to get the request header, recommended way is `req.get('authorization')` – Bo Lu Mar 14 '19 at 21:27
  • 4
    @BoLu recommended by whom? – robertklep Mar 15 '19 at 08:00
  • 1
    it is in the [Express official doc](https://expressjs.com/en/4x/api.html#req.get) – Bo Lu Mar 16 '19 at 09:47
  • 4
    @BoLu but it doesn't say anywhere that using `req.get` is recommended. The same documentation also states that _"The req object is an enhanced version of Node’s own request object and supports all built-in fields and methods"_, so I don't see a reason why someone shouldn't be using `req.headers`. – robertklep Mar 16 '19 at 10:13
  • True, you can use `req.headers`. But here we're talking about Express and `req.get` just appears at the top level of its document, so it should be the idiomatic way to get request headers when using Express. – Bo Lu Mar 17 '19 at 10:01
  • 1
    I want to use this but it messes up the catch-all 404 I have at the bottom of my script https://stackoverflow.com/questions/11500204/how-can-i-get-express-js-to-404-only-on-missing-routes – Philip Kirkbride Apr 19 '19 at 17:57
  • 3
    @PhilipKirkbride in that case, add the proposed middleware separately to each route that needs to be protected (`app.use('/protected', auth_middleware, handler)`). Alternatively, you can prefix the routes that need to be protected and use a separate `express.Router` (`app.use('/protected', auth_middleware, router)`) ([example here](https://gist.github.com/robertklep/d4095a127671ee2da65c046208bed8ad)). – robertklep Apr 20 '19 at 05:57
  • @robertklep thanks, writing a middleware function was easier than I expected! – Philip Kirkbride Apr 20 '19 at 12:44
  • 1
    For those using typescript, please don't use the `req.get(item: string)` suggestion for anything. It can not be inspected, it won't return proper types, and -- in general -- using string ids offer zero feedback when debugging. – Seph Reed Mar 23 '20 at 17:05
  • A year later, I'm at this problem again from a different side. `req.get()` is case insensitive, and that can sometimes save you from confusing errors. – Seph Reed Sep 28 '21 at 16:54
3
const token = req.headers.authorization.split(' ')[1];
if(!token) return res.send("No credentials");
// next(); // Let the user proceed
MD SHAYON
  • 7,001
  • 45
  • 38
  • 3
    Your answer could be improved by adding more information on what the code does and how it helps the OP. – Tyler2P Aug 02 '22 at 09:53
1

Here is a solution with a more modular approach to chain validations, creating a middleware with a validator library specifically designed for express: express-validator.

Example of expected header Authorization: Bearer c8f27fee2a579fa4c3fa580

  1. Install express-validator package:

    npm install --save express-validator OR yarn add express-validator


  1. Create a middleware (e.g. in path src/middlewares/validators.js)
import { header, validationResult } from "express-validator";

export const myRequestHeaders = [
  header('authorization')
    .exists({ checkFalsy: true })
    .withMessage("Missing Authorization Header") // you can specify the message to show if a validation has failed
    .bail() // not necessary, but it stops execution if previous validation failed
    //you can chain different validation rules 
    .contains("Bearer")
    .withMessage("Authorization Token is not Bearer")
];

export function validateRequest(req, res, next) {
  const validationErrors = validationResult(req);
  const errorMessages = [];

  for (const e of validationErrors.array()) {
    errorMessages.push(e.msg);
  }

  if (!validationErrors.isEmpty()) {
    return res.status(403).json({ "errors": errorMessages });
  }
  next();
}


  1. use validator middlewares in your endpoint.

    IMPORTANT: you need use the middlewares before your actual route function. Also, you need to chain the middleware such that the validateRequest function (which actually verifies the validity of your request) comes after the expected header validator, in this case myRequestHeader. See below:

app.use('/api/v1/your-endpoint', myRequestHeaders, validateRequest, async (req, res) => {
  // the validator middleware will have already thrown a 403 if the header was missing,
  // so you can be 100% sure that the header is present with validations your created.
  console.log("req.headers.authorization", req.headers.authorization);
  
  // do whatever you want
  const actualToken = getBearerTokenFromHeader(req.headers.authorization); // c8f27fee2a579fa4c3fa580

  res.sendStatus(200);
})

// helper function to get token value
const getBearerTokenFromHeader = (authToken) => {
  return authToken.split(" ")[1]
}

With this library you can check the presence and quality of headers, parameters, body data and so on.

Fed
  • 1,696
  • 22
  • 29