Reputation: 101
My issue is that on POST, I'm getting a 'Missing Credentials' error whenever I try and authenticate using 'local' strategy. I initially thought that this was an issue with body-parser, but even after some suggestions about forcing urlencoded: true/false, I'm still not getting any differing results. My console logs are also not returning anything substantial beyond irrelevant req.body's, and I'm really not sure what else to do at this point. I've read about this being a potential issue with specific versions of Express, but I've tried downgrading/upgrading and that hasn't changed anything.
With some breakpoints, it looks like my new localStrategy in PassportConfig.js is never being called, but I'm not entirely sure if that's my error or not? Would really appreciate some pointers here.
app.js
require("./config/config");
require("./models/db");
require("./config/passportConfig");
const express = require("express");
const bodyParser = require("body-parser");
const cors = require("cors");
const passport = require("passport");
// router
var rtsIndex = require("./routes/index.router");
var app = express();
// middleware config
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(cors());
app.use(passport.initialize());
app.use(passport.session());
app.use("/api", rtsIndex);
My PassportConfig.js:
const passport = require("passport");
const LocalStrategy = require("passport-local").Strategy;
const mongoose = require("mongoose");
var User = mongoose.model("User");
passport.use(
new LocalStrategy({ usernameField: "email" }, (username, password, done) => {
User.findOne({ email: username }, (err, user) => {
if (err) return done(err);
// if user is unknown
else if (!user)
return done(null, false, {
message: "That email address has not been registered"
});
// or if password is wrong
else if (!user.verifyPassword(password))
return done(null, false, { message: "Wrong password" });
// successful authentication
else return done(null, user);
});
})
);
My controller for a basic user registration/login system:
const mongoose = require("mongoose");
const passport = require("passport");
const _ = require("lodash");
User = require("../models/user.model");
// snipped some registration code for brevity
module.exports.authenticate = (req, res, next) => {
console.log(req.body);
// call for passport authentication
// executed passport.use from passportConfig
passport.authenticate("local", (err, user, info) => {
// Error from passport middleware
if (err) return res.status(400).json(err);
// user is registered if there is a value in 'user'
else if (user) return res.status(200).json({ token: user.generateJwt() });
// user unkown or wrong password
else return res.status(401).json(info);
})(req, res);
};
Edit: Added my user model definitions as well:
const mongoose = require("mongoose");
const bcrypt = require("bcryptjs");
const jwt = require("jsonwebtoken");
var userSchema = new mongoose.Schema({
fullName: {
type: String,
required: "Full name cannot be empty"
},
email: {
type: String,
required: "Email cannot be empty",
unique: true
},
password: {
type: String,
required: "Password cannot be empty",
minlength: [4, "Password must be at least 4 characters long"]
},
// Encryption/Decryption of password
saltSecret: String
});
// Custom validation for email
userSchema.path("email").validate(val => {
emailRegex = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
return emailRegex.test(val);
}, "Invalid email");
// Events
userSchema.pre("save", function(next) {
bcrypt.genSalt(10, (err, salt) => {
bcrypt.hash(this.password, salt, (err, hash) => {
this.password = hash;
this.saltSecret = salt;
next();
});
});
});
// Methods
userSchema.methods.verifyPassword = function(password) {
// plain password, vs encrypted password,
// called from findOne in passportConfig
return bcrypt.compareSync(password, this.password);
};
userSchema.methods.generateJwt = function() {
return jwt.sign({ _id: this._id }, process.env.JWT_SECRET, {
expiresIn: process.env.JWT_EXP
});
};
// Can pass custom name as third paramater
// Modified with a force export for debugging
module.exports = mongoose.model("User", userSchema);
And finally, some basic routing:
const express = require("express");
const router = express.Router();
const ctrlUser = require("../controllers/user.controller");
// foreword all links with api/
router.post("/register", ctrlUser.register);
router.post("/authenticate", ctrlUser.authenticate);
// Export router for middleware so app.js can access it
module.exports = router;
I've tried the following solutions to these questions:
With it not being the body-parser issue, I thought it might be a usernameField issue, but that doesn't seem to be the case either.
Upvotes: 0
Views: 355
Reputation: 370
I Have tried out your code which you mention above. And i have made little change and remove some validation. I mention all files below:
passportMiddleware.js
import passportLocal from 'passport-local';
const localStrategy = passportLocal.Strategy;
import userModel from '../model/userModel';
import passport from 'passport';
import bcrypt from 'bcrypt';
passport.use(new localStrategy({
usernameField: 'email',
}, async (email, password, done) => {
const user = await userModel.findOne({ "email": email });
// console.log(user)
if (!user) {
return done(null, false);
}
try {
if (password === user.password) {
return done(null, user)
} else {
return done(null, false, 'password Incorrect')
}
} catch (error) {
return done(error)
}
}));
userController.js
import userModel from '../model/userModel';
import bcrypt from 'bcrypt';
import passport from 'passport';
exports.authenticate = (req, res, next) => {
res.status(200).json({ messaage: 'Authenticated', data: req.user
});
};
userModel.js
import mongoose from 'mongoose';
import bcrypt from 'bcrypt';
import jwt from 'jsonwebtoken';
var userSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
email: {
type: String,
required: true,
unique: true
},
password: {
type: String,
required: true,
}
});
// Events
userSchema.pre("save", function (next) {
bcrypt.genSalt(10, (err, salt) => {
bcrypt.hash(this.password, salt, (err, hash) => {
this.password = hash;
this.saltSecret = salt;
next();
});
});
});
module.exports = mongoose.model("Users", userSchema);
routes.js
router.post('/authenticate', passport.authenticate('local', { session: false }), userController.authenticate)
;
Try this out. Import the dependency first.
once you get success to authenticate using passport-local. you can make changes according to your criteria.
I have import dependencies in ES6 format. You just change it to ES5.
Upvotes: 1