user2338041
user2338041

Reputation: 71

Node Express pg-promise Passport-local JWT Cannot Post

I'm a Node beginner who's trying to get up and running by following some tutorials and examples. I want to build a JSON API that allows local, facebook, twitter and google login so I'm using passport.js. Currently I'm trying to merge https://github.com/vitaly-t/pg-promise-demo with http://blog.slatepeak.com/refactoring-a-basic-authenticated-api-with-node-express-and-mongo/ (sorry the code is such a mismatched mess of styles) and am running into an issue where my log in POST returns: Cannot Post /auth/login/local when I provide the correct credentials, it works fine if my credentials are bad I really don't understand why the behavior would be different, unless passport-local is doing something in the background that I'm not taking into consideration. The files that I think are relevant are:

SERVER.js

'use strict';

var db = require('./db/db.js').db
    , express = require('express')
    , auth = require('./routes/auth')
    , router = express.Router()
    , app = express()
    , routes = require('./routes/routes.js')
    , bodyParser = require('body-parser') //must be before passport
    , config = require('./conf/main'); 

app.use(bodyParser.urlencoded({ extended: true })); 
app.use(bodyParser.json());
app.use('/auth', auth);

// Enable CORS from client-side - Will I need this?
app.use(function(req, res, next) {  
  res.header("Access-Control-Allow-Origin", "*");
  res.header('Access-Control-Allow-Methods', 'PUT, GET, POST, DELETE, OPTIONS');
  res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization, Access-Control-Allow-Credentials");
  res.header("Access-Control-Allow-Credentials", "true");
  next();
});

//////////////////////////////////////////////
// Users Web API
//////////////////////////////////////////////
app.post('/users/local', routes.userAddLocal);
app.get('/users', routes.getAllUsers); //This should go into the Admin App. Will they both be able to use Postgres at once?


app.listen(config.port, () => {
    console.log('\nReady for requests on http://localhost:' + config.port);
});

AUTH.js

'use strict';
///////////////////////// Passport Setup

var passport = require('passport')
    , LocalStrategy = require('passport-local').Strategy
    , JwtStrategy = require('passport-jwt').Strategy
    , ExtractJwt = require('passport-jwt').ExtractJwt
    , jwt = require('jsonwebtoken')  
    , db = require('../db/db.js').db
    , config = require('../conf/main')
    , express = require('express')
    , bodyParser = require('body-parser') //must be before passport
    , bcrypt = require('bcrypt-nodejs');

const jwtOptions = {  
  // Telling Passport to check authorization headers for JWT
  jwtFromRequest: ExtractJwt.fromAuthHeader(),
  // Telling Passport where to find the secret
  secretOrKey: config.secret
};

passport.use('local', new LocalStrategy({
    usernameField: 'email'
    , passReqToCallback: false
    , session: false //Turn off sessions, this is a stateless REST service using JWT
  },
  function(username, password, done) {
    db.users.findByEmail(username)
        .then(function (user){
            if (!user) { return done(null, false); }
            var pwMatch = bcrypt.compareSync(password, user.password);
            if (!pwMatch) { return done(null, false); }
            return done(null, user);
        })
        .catch(function(error){
            return done(error);
        })
    }
));

passport.use('jwt', new JwtStrategy(jwtOptions, function(payload, done) {  
     console.log("jwt strategy top line");
    db.users.findById(payload._id) //maybe need payload.doc._id or payload.document._id
        .then(function (user){
           if (user) {
                done(null, user);
            } else {
                done(null, false);
            } 
        })
        .catch(function(error){
            return done(err, false);
        })
}));

function generateToken(user) {  
  return jwt.sign(user, config.secret, {
    expiresIn: 10080 // in seconds
  });
};

var authRoutes = express.Router();
authRoutes.use(bodyParser.urlencoded({ extended: true })); 
authRoutes.use(bodyParser.json());
authRoutes.use(passport.initialize());  

authRoutes.post('/login/local', passport.authenticate('local', {
    function(req, res) {
        var tokenObj = auth.generateToken({email: req.body.email, displayName: req.body.DisplayName});
        res.status(201).json({
            token: 'JWT ' + tokenObj,
            user: req.body.displayName
        });
    },
        failureRedirect: '/auth/notauth',
        session: false
    }));

authRoutes.get('/notauth', function (req, res){
    res.status(401).send("Login Failed");
});

module.exports = authRoutes;
module.exports.generateToken = generateToken;

Any help anyone could offer would be much appreciated.

Edit #1 :I tried rewriting the route for PORT ('login/local') and now it logs in but for some reason I don't have the req and req.user objects available to me. Everything I google says that I need to include sessions, but I'm not using sessions, so this can't be right for me. Here's the new route:

authRoutes.post('/login/local', passport.authenticate('local',
    function(req, res){
        console.log("authentication successful");
        var tokenObj = generateToken({email: req.body.email, displayName: req.body.DisplayName});
        res.status(201).json({
            token: 'JWT ' + tokenObj,
            user: req.body.displayName
        });
    }
    , {failureRedirect: '/auth/notauth'}
    , {session: false}
));

Can someone tell me how to fix this so that req.user will be available as per the Passport documentation?

Edit #2: It seems that the success function is getting a null and an object which contains a db record from the users table. Seems like exactly what I return when the user successfully logs on. Somehow I've broken Passport-Local and it's not doing that.

Upvotes: 2

Views: 811

Answers (1)

user2338041
user2338041

Reputation: 71

Got it to work with some changes:

passport.use('local', new LocalStrategy({
    usernameField: 'email'
   // , passReqToCallback: false
    , passReqToCallback: true
    , session: false //Turn off sessions, this is a stateless REST service using JWT
  },
  //function(username, password, done) {
      function(req, username, password, done) {
    db.users.findByEmail(username)
        .then(function (userRecord){
            if (!userRecord) { return done(null, false); }
            var pwMatch = bcrypt.compareSync(password, userRecord.password);
            if (!pwMatch) { return done(null, false); }
            //return done(null, userRecord);
             return done(null, req, userRecord);
        })
        .catch(function(error){
            return done(error);
        })
    }
));

and

authRoutes.post('/login/local', passport.authenticate('local',
    function(err, req, userRecord){  
        console.log("authentication successful");
        var tokenObj = generateToken({email: userRecord.email, displayName: userRecord.DisplayName});
        var res = req.res; //nfi why i need this
        res.status(201).json({
            token: 'JWT ' + tokenObj,
            user: userRecord.displayName
        });
    }
    , {failureRedirect: '/auth/notauth'}
    , {session: false}
));

Upvotes: 2

Related Questions