How to pass PassportJS message to React frontend in Express - javascript

I have been working with passport-local for user authentication and have been struggling in sending custom messages like "Username doesn't exist" or "Password doesn't match" from the server to the React frontend.
const user = await User.findOne({ email });
if (!user) {
console.log("No user`");
return done(null, false, {
message: "User with this email ID doesn't exist",
});
}
The current method I have is a very hacky one and just passes the passport.authenticate function inside an express function.
authRouter.post("/login", (req, res, next) => {
passport.authenticate("local", (err, user, info) => {
const error = err as Error;
if (error) {
return res.status(500).json({ message: error.message });
}
if (info) {
return res.status(400).json({ message: info.message });
}
req.logIn(user, (error) => {
if (error) {
return res.status(500).json({ message: error.message });
}
return res.json(user);
});
})(req, res, next);
});
Is there a better way to do this? I also tried using connect-flash with passport but wasn't able to quite get it working.

this is my code:
passport.use(new LocalStrategy(
const user = await User.findOne({ email });
if (!user) {
console.log("No user`");
return done(null, false, {
message: "User with this email ID doesn't exist",
});
}
authRouter.post("/login", passport.authenticate( "local", {
successReturnToOrRedirect: "Your_route",
failureRedirect: "/login",
failureMessage: true,
}))
))
and when you want send failure message to backend:
if (req.session.messages) {
//you can send req.session.message[0]
}
if you have session-express,you should delete session after sending req.session.message[0]:
delete req.session.messages
req.session.save()

Related

passport.js authenticate throwing unauthorized error?

I am trying to use passport.js with mongoose. The data sent are correct but I get an error of code 401 saying unauthorized?
Here is the back end controller.
userController.login = (req, res) =>{
console.log("recieved");
console.log(req.body);
const user = new Users({
username: req.body.mail,
password: req.body.password
})
req.login(user, (err) => {
if (err){
res.status(404).send(err);
}else{
console.log("user found");
passport.authenticate("local", (err, user, info) => {
if (err) {
return res.status(401).json(err);
}
if (user) {
return res.status(200).send(user);
} else {
res.status(401).json(info);
}
})(req, res, () => {
console.log("Authenticated!");
res.status(200).send(user);
});
}
})
}
While posting I needed to rename the req.body.mail to just req.body.username because auth and req.login looks for req body directly and search for a username object.

How to Pass Credentials to passport.authenticate?

I'm trying to secure an API endpoint on a node.js express app that uses passport.
This is my route:
router.post('/api/devices', authController.isAuthorized, catchErrors(deviceController.getDevicesAPI));
This is my authorization method:
exports.isAuthorized = (req, res, next) => {
passport.authenticate('local', {session: false}, (err, user, info) => {
if (err || !user) {
return res.json({ message: 'Something is not right ', err, info });
}
req.login(user, {session: false}, (err) => {
if (err) {
res.send(err);
}
next();
});
})(req, res);
};
From Postman or a separate local server, I get the response:
{
"message": "Something is not right ",
"err": null,
"info": {
"message": "Missing credentials"
}
}
This is the Postman configuration:
What am I missing?
How is your local strategy configured? It seems like a database query problem
As the sample in http://www.passportjs.org/docs/username-password/, please see my comments below
var passport = require('passport')
, LocalStrategy = require('passport-local').Strategy;
passport.use(new LocalStrategy(
function(username, password, done) { //<--- Here is where you pass the UN&PASS
User.findOne({ username: username }, function(err, user) { //<--- Here is the sample code that should find you a user
if (err) { return done(err); } //<--- Here could be where the response is coming from
if (!user) {
return done(null, false, { message: 'Incorrect username.' });
}
if (!user.validPassword(password)) {
return done(null, false, { message: 'Incorrect password.' });
}
return done(null, user); //<--- Here is the sample code that should let you return that user
});
}
));
I finally dug it out from here. User.authenticate() is the method I was looking for.
exports.isAuthorized = async (req, res, next) => {
const username = req.body.username;
const password = req.body.password;
const user = await User.findOne({ email: username });
if (!user) {
res.sendStatus(403);
return;
}
user.authenticate(password, function(err, result) {
if (result) {
next();
return;
}
res.sendStatus(403);
});
};

User Authentication error - Passport.js

Im using passport.js (local strategy) to authenticate users but I am getting the following error:
Unhandled "error" event. (Incorrect arguments)
Detailed Error Message:
index.js file:
const {register: registerUser, login: loginUser} = require('../controllers/authentication');
// login
router
.route('/login')
.post(loginUser);
Authentication.js file:
// login function
let login = (req,res) => {
if(!req.body.email || !req.body.password){
sendJsonResponse(res, 400, {"message": "All fields required"});
return;
}
passport.authenticate('local', (err, user, info) => {
let token;
if(err){
sendJsonResponse(res, 404, err);
return;
}
if(user){
token = user.generateJwt();
sendJsonResponse(res, 200, {
"token": token
});
} else {
sendJsonResponse(res, 401, info);
}
})(req,res);
};
Passport.js File:
const passport = require('passport');
const LocalStrategy = require('passport-local').Strategy;
const mongoose = require('mongoose');
const User = mongoose.model('Users');
passport.use(new LocalStrategy({
usernameField: 'email',
}, function (email, password, done) {
User.findOne({'email': email}, function(err, user) {
if(err){
return done(err);
}
if(!user){
return done(null, false, {message: 'Incorrect Username.'});
}
if(!user.validPassword(password)){
return done(null, false, {message: 'Incorrect Password.'});
}
return done(null, user)
});
}
));
I can't seem to figure out what the issue might be. I've tried checking typeof for both email and password and they are indeed Strings. Does anybody know what could be the issue.

Why isn't .save for Mongoose working in ReactJS?

I have a ReactJS and Redux connected to MongoDB, Mongoose.
I have a Mongoose Schema (user.js) set up like so:
var UserSchema = new Schema({
email: {
type: String,
lowercase: true,
unique: true,
required: true
},
})
And a API controller that receives the email string request, and then if nothing is entered in the text field, it sends a 422 error, and inside User.findOne, if the email already exists in the database, then it throws a 422 error and if not, does user.save to save it in the database.
"use strict";
const User = require('../models/user')
exports.register = function(req, res, next) {
const email = req.body.email;
console.log('ERROR 1')
if(!email) {
return res.status(422).send({ error: 'You must enter an email address.'})
console.log('ERROR 1')
}
User.findOne({ email: email }, function(err, existingUser) {
if(err) { return next(err); }
console.log('ERROR 2')
if(existingUser) {
return res.status(422).send({ error: 'That email address is already in use.'})
}
console.log('ERROR 3')
let user = new User({
email: email,
})
console.log('ERROR 4')
user.save(function(err, user) {
if(err) { return next(err); }
console.log('ERROR 5')
res.status(201).json({
user: user,
})
})
})
console.log('ERROR 6')
}
And I am making a POST request as such:
export function registerUser({ email }) {
return function(dispatch) {
axios.post(`${API_URL}/auth/register`, { email })
.then(response => {
console.log('THIS IS TESTING PURPOSE')
console.log(response)
dispatch({ type: AUTH_USER });
})
.catch((error) => {
errorHandler(dispatch, error.response, AUTH_ERROR)
});
}
}
I made several POST requests and all get successful status back from API with sever config: {'database': 'mongodb://localhost/practicedb',
'port': process.env.PORT || 3000}, yet the data never gets saved and database (practicedb) doesn't show up on Terminal.
Everything seem to be set up correctly but why the problem? Could I be missing something? Any insight or guidance would be really appreciated.
Thank you in advance!
Here are some logs and what's OPTIONS request that I never made:
Tried registering with same email again:
Correct if i'm wrong but your bare save method is not async. Save method return a promise. See http://mongoosejs.com/docs/promises.html
EDIT
user.save().then(function(doc) {
if (!doc) { next(new Error('Error while persisting!')); }
console.log('ERROR 5');
res.status(201).json({
user: doc
});
});
You can also achieve this with any promised library (Q, bluebird) or use ES6 Promise. Alternatively use async.
Example with Q. NOT TESTED:
"use strict";
const User = require('../models/user');
const Q = require('Q'); //add https://github.com/kriskowal/q
exports.register = function(req, res, next) {
const email = req.body.email;
console.log('ERROR 1')
if(!email) {
return res.status(422).send({ error: 'You must enter an email address.'})
console.log('ERROR 1')
}
var deferred = Q.defer();
User.findOne({ email: email }, function(err, existingUser) {
if(err) { return next(err); }
console.log('ERROR 2')
if(existingUser) {
return res.status(422).send({ error: 'That email address is already in use.'})
}
console.log('ERROR 3')
let user = new User({
email: email,
})
console.log('ERROR 4')
user.save(function(err, user) {
if(err) {
deferred.reject(err);
return next(err);
}
console.log('ERROR 5')
deferred.resolve(user); //
});
res.status(201).json({
user: deferred.promise,
})
})
console.log('ERROR 6')
}

Express, passport: can't set headers after they are sent

I'm still a noob to node and webdev, but trying hard!
I get this error : can't set headers after they are sent
with the following while using passport.js and bcryptjs compare method for password validation on a mean stack
routes/login.js
var express = require('express')
var router = express.Router()
var mongoose = require('mongoose')
var User = mongoose.model('User')
var passport = require('passport')
router.post('/', function (req, res, next){
passport.authenticate('local', function(err, user, info){
if(err){ return next(err); }
if(user){
return res.json({token: user.generateJWT()});
} else {
return res.status(401).send(info)
}
})(req, res, next);
});
module.exports = router
authenticate/local.js
var passport = require('passport')
var LocalStrategy = require('passport-local').Strategy
var mongoose = require('mongoose')
var User = mongoose.model('User')
var bcrypt = require('bcryptjs')
passport.use(new LocalStrategy(function(username, password, done) {
User.findOne({
username: username
}, function(err, user) {
if (err) {
return done(err)
}
if (!user) {
return done(null, false, {
message: {
username: 'Incorrect username.'
}
})
}
bcrypt.compare(password, user.password, function(err, isMatch) {
if (err) {
return done(err)
}
if (!isMatch) {
return done(null, false, {
message: {
password: 'Incorrect password'
}
})
}
});
return done(null, user);
});
}));
This validates correctly for a valid username and password, and logs in.
For an invalid username, it correctly rejects the login attempt.
But for an invalid password, it logs in and then crashes the app with the Can't set headers error.
However if i change the bcrypt.compare to bcrypt.compareSync, then all validations are correct.
if (!bcrypt.compareSync(password, user.password)) {
return done(null, false, {
message: {
password: 'Incorrect password'
}
});
}
I would rather not depend on the sync methods, so help me please understand where I am going wrong!
bcrypt.compare() is async but you're calling done(null, user) immediately. Move it inside the callback and it should be fine:
bcrypt.compare(password, user.password, function(err, isMatch) {
if (err) { return done(err) }
if (!isMatch) {
return done(null, false, { message: { password: 'Incorrect password' } })
}
done(null, user)
})

Categories

Resources