swagger-api / swagger-node

Swagger module for node.js
http://swagger.io
Apache License 2.0
3.97k stars 584 forks source link

How to set up JWT authentication via Swagger definition? #364

Open nakamorichi opened 8 years ago

nakamorichi commented 8 years ago

After generating a swagger-node project (in my case Hapi), how do I configure JWT authentication for the routes (preferably by using hapi-auth-jwt2 or similar plugin)? Also, what is the preferred way to handle route authorization?

tanzim commented 8 years ago

Hi, It's a two step process. You'll first have to define in your swagger spec[1] how your endpoint is secured (via and api key for example) and then in your server code, you'll have to implement a security handler for it [2]. remember that Swagger doesn't explicitly know about JWT (JWT is just another way of encoding a token) so you'll have to teach it to work with it :)

You could use apiKey and name the header Authorization, which you can read as req.headers.authorization from the req option of the security handler, extract the value, and parse it using a JWT library[1] and that's about it.

[1] http://swagger.io/specification/#securitySchemeObject [2] https://github.com/swagger-api/swagger-node/issues/228#issuecomment-163805253 [3] https://github.com/auth0/node-jsonwebtoken

jlyon commented 8 years ago

This code worked for me

var ejwt = require('express-jwt');

// Initialize express-jwt
var jwt = ejwt({
  secret: new Buffer(process.env.AUTH0_CLIENT_SECRET, 'base64'),
  audience: process.env.AUTH0_CLIENT_ID
});

// Set up the token security handler
var config = {
  appRoot: __dirname, // required config
  swaggerSecurityHandlers: {
    token: function(req, authOrSecDef, scopesOrApiKey, cb) {
      jwt(req, req.res, function(err) {
        if (req.user == undefined) {
          return cb(new Error('access denied - user does not exist in auth0'));
        }
        else {
          console.log(req.user); // Contains { iss: 'https://xxx.auth0.com/', sub: 'auth0|xxx', ... }
          return cb(null);
        }
      }
    }
  }
};
davidascher commented 8 years ago

@jlyon: thanks -- note for whoever follows this: there's a missing ) in the above pasted code to close off the jwt(… block.

amcdnl commented 8 years ago

This is how we did it:

import * as fs from 'fs';
import * as path from 'path';

import * as swaggerTools from 'swagger-tools';
import * as passport from 'passport';

export function setupSwagger(app) {
  // resolve the spec
  const spath = path.resolve('./dist/spec.json');
  const file = fs.readFileSync(spath, 'utf8');
  const spec = JSON.parse(file);

  // setup middleware swagger middleware in express
  swaggerTools.initializeMiddleware(spec, (middleware) => {
    app.use(middleware.swaggerUi());
    app.use(middleware.swaggerMetadata());
    app.use(setupSwaggerSecurity(middleware));
    app.use(middleware.swaggerValidator({
      validateResponse: true
    }));
  });
};

function setupSwaggerSecurity(middleware) {
  return middleware.swaggerSecurity({
    jwt_token: (req, authOrSecDef, scopes, callback) => {
      passport.authenticate('jwt', { session: false }, (err, user, info) => {
        if(err) callback(new Error('Error in passport authenticate'));
        if(!user) callback(new Error('Failed to authenticate oAuth token'));
        req.user = user;
        return callback();
      })(req, null, callback);
    }
  });
};

then in passport:

import * as config from 'config';
import * as passport from 'passport';
import { Strategy as JwtStrategy, ExtractJwt } from 'passport-jwt';

let opts = {
  jwtFromRequest: ExtractJwt.fromAuthHeader(),
  secretOrKey: config.get('auth.jwt_secret').toString()
};

function verify(payload, done) {
  const id = payload.sub;
  return id !== undefined;
  /*
  User.findOne({ id: jwt_payload.sub }, (err, user) => {
      if (err) return done(err, false);
      if (user) {
        done(null, user);
      } else {
        done(null, false);
        // or you could create a new account
      }
  });
  */
};

export function setupAuth(app) {
  app.use(passport.initialize());
  passport.use(new JwtStrategy(opts, verify));
};

Demo project here: https://github.com/swimlane/node-microservice-demo/tree/master/petstore