JWT verification for Swagger NodeJs API middleware

Viewed 356

I generated with Swagger a Node js API. I'm trying to add a jwt token check in my API to allow access to protected ressources. I would like to use something similar to passport js but I don't understand where I need to add the "passport.authenticate" method.

This is the way the app is initialized:

function initializeApp(swaggerOption, swaggerDoc) {

const p = new Promise((resolve, reject) => {
  swaggerTools.initializeMiddleware(swaggerDoc, function (middleware) {

    // Interpret Swagger resources and attach metadata to request - must be first in swagger-tools middleware chain
    app.use(middleware.swaggerMetadata());

    // Validate Swagger requests
    app.use(middleware.swaggerValidator());

    // Route validated requests to appropriate controller
    app.use(middleware.swaggerRouter(swaggerOption));

    // Serve the Swagger documents and Swagger UI
    app.use(middleware.swaggerUi());

    resolve(app);
    })
  });
  return p;
}
  exports.initializeApp = initializeApp;

  initializeApp(options, swaggerDoc ).then((app) => {
  // Start the server
  http.createServer(app).listen(serverPort, function () {
  console.log('Your server is listening on port %d (http://localhost:%d)', serverPort,     serverPort);
  console.log('Swagger-ui is available on http://localhost:%d/docs', serverPort);
  });
})

And an example of a controller

'use strict';

var utils = require('../utils/writer');
var Auth = require('../service/AuthService');

module.exports.authenticatePUT = function authenticatePUT (req, res, next) {
    var body = req.swagger.params['body'].value;
    Auth.authenticatePUT(body)
    .then(function (response) {
    utils.writeJson(res, response);
    })
    .catch(function (response) {
    utils.writeJson(res, response);
    });
  }

Since there is no route like so I can siply follow passport js's syntax

app.get ('/ profile',
  passport.authenticate ('bearer', {session: false}),
  function (req, res) {
    res.json (req.user);
  });

and no documentation (or I don't find) for middleware methods I don't find where I can add the jw token verification. If someone has some examples or explanations I would be verry happy :)

Thanks !

2 Answers

I am not using passport, but a simple example is like so:

In your routes the middleware is defined just as a function to run before proceeding further. It will implicitly receive the request data.

 app.post(
    '/api/object/create',
    auth.getToken, // << middleware evaluate
    objectController.create
  )

I have created my own middleware evaluation, it looks like this:

getToken: function (req, res, next) {
    const bearerHeader = req.headers['authorization']

    // Get the bearer token from the request
    if (typeof bearerHeader !== 'undefined') {
      const bearer = bearerHeader.split(' ')
      const bearerToken = bearer[1]
      req.token = bearerToken
      next()
    } else {
      res.sendStatus(401)
    }
  }

Now, this only checks to see if we have a token at all, it is a pretty simple check and is not in any way a security check. This is because we may want to do more checks at the objectController.create function, about who this is, and what they should be able to do next. These checks could also be done within the above function, depending on your use case.

objectController.create receives the request, and the first thing it does is check the token

const currentUser = await currentUser(req.token)

which checks if this user is someone in the database, based on decoding from the jwt signing secret

async currentUser(token) {
    const decoded = jwt.verify(token, process.env.USER_AUTH_SECRET) // << very important never to commit this as a readable value in your repo, store in a local environment variable
    const user = await User.findOne({
      where: { email: decoded.user.email },
    })
    console.log(user.email)
    return user
  },

So, whilst this is not a Passport solution, it hopefully shows the basic process of middleware and then authenticating a jwt, which I think was your general question.

I think you want something like this:

const fs = require('fs');
const swaggerTools = require('swagger-tools');
const path = require('path');
const jsYaml = require('js-yaml');
const passport = require('passport');
const cors = require('cors');
const CONSTANTS = require('./constants');

module.exports = function initializeSwagger(app) {
// swaggerRouter configuration
    const swaggerRouterOptions = {
        swaggerUi: path.join(__dirname, '/swagger.json'),
        controllers: path.join(__dirname, '../controllers'),
        useStubs: process.env.NODE_ENV === 'development' // Conditionally turn on stubs (mock mode)
    };

    const swaggerDoc = jsYaml.safeLoad(fs.readFileSync(path.join(__dirname, '../api/swagger.yaml'), 'utf8'));

// Initialize the Swagger middleware
    swaggerTools.initializeMiddleware(swaggerDoc, function (middleware) {
        // Interpret Swagger resources and attach metadata to request - must be first in swagger-tools middleware chain
        app.use(middleware.swaggerMetadata());


        // *** Where you will call your JWT security middleware ***
        app.use(initializeSwaggerSecurity(middleware));


        //enable CORS
        app.use(cors());
        // Validate Swagger requests
        app.use(middleware.swaggerValidator());
        // Route validated requests to appropriate controller
        app.use(middleware.swaggerRouter(swaggerRouterOptions));
        // Serve the Swagger documents and Swagger UI
        if(process.env.NODE_ENV === "prod"){
            app.use('/docs', (req, res, next) => {
            });
          } else {
            app.use(middleware.swaggerUi());
          }
    });
};

function initializeSwaggerSecurity(middleware) {
    return middleware.swaggerSecurity({
        jwtAuth: (req, authOrSecDef, scopes, callback) => {
            passport.authenticate('jwt', {session: false}, (err, user, info) => {
                if (err) {                   
                    return callback(new Error(CONSTANTS.AUTHENTICATION.ERROR_MESSAGE_DEFAULT))
                };
                if (!user) {
                    // no user session, were we tampered? What happened? 
                    // @param info has that detail!
                    // console.log('url requested: ' + req.url + ' | raw headers: ' + req.rawHeaders);
                    // console.log('api: passport => jwt fn() initializeSwaggerSecurity(), rejected jwt token, token tampered or user session does not exist; failed to authenticate token: ', info);
                    return callback(new Error(CONSTANTS.AUTHENTICATION.ERROR_MESSAGE_TOKEN))
                }
                else {
                    req.user = user;
                    return callback();
                }
            })(req, null, callback);
        }
    });
};


Hope this helps you or anybody else who might be looking to implement this on Swagger 2.0

Related