Reputation: 71
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
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