How to use access messages from your passportjs strategy in React - javascript

If you have a react application and don't wanna use connect-flash to access the messages generated by the local strategy you can use the following:
My passport local strategy, I am using a sqlite db:
passport.use(
new LocalStrategy({ usernameField: 'name' },
(name, password, done) => {
//Match user
db.get(`SELECT * FROM Users WHERE name = '${name}'`, (err, user) => {
if(err) throw err;
if(!user) {
return done(null, false, { message : 'Username not registered' });
}
//Match password
bcrypt.compare(password, user.password, (err, isMatch) => {
if(err) throw Error;
if(isMatch) {
done(null, user, { message : 'Log in successful' });
} else { done(null, false, { message : 'Password incorrect' })
}
});
})
})
);

You can use a custom call back function for your /login route:
app.post('/login', (req, res, next) => {
passport.authenticate('local', (err, user, info) => {
if (err) { return next(err); }
if (!user) { return res.json(info); }
req.logIn(user, (err) => {
if (err) { return next(err); }
return res.json(info);
});
})(req, res, next);
});
The info argument contains the object passed as a third argument in done() from the strategy and can be added to the body of res and therefore used by react.

Related

Cannot access property of req.user without iterating

I'm havig trouble understanding how to access the properties of a global variable that is set in the middleware of my application
// app.js
app.use(function (req, res, next) {
res.locals.user = req.user || null;
next();
});
I would have thought I could access the req.user.username in my template (using handlebars), but for some reason it is forcing me to iterate over this object.
A consle.log(req.user) shows:
_id: 5f01f9a861f5b33b42a9e,
username: 'onetap',
email: 'test#gmail.com',
password: '$2b$10$VLBS8ZwPKiaXasdfsiiwfg.wyJ1J5CwTKLjS5zXwAsvukHpNmk0HG2',
createdAt: 2020-07-05T16:02:48.081Z,
__v: 0
}
And in my template I have to use an each loop and can't access the properties directly. the req.user is not an array either.
{{user.username}}//undefined
{{#each user}}
{{username}} //onetap
{{/each}}
passport.js file
const LocalStrategy = require("passport-local").Strategy;
const bcrypt = require("bcrypt");
const mongoose = require("mongoose");
// Load User Model
const User = require("../models/User");
module.exports = function (passport) {
passport.use(
new LocalStrategy({ username: "username" }, (username, password, done) => {
// Match User
User.findOne({
username: username,
}).then((user) => {
if (!user) {
return done(null, false, { message: "Invalid Username" });
}
console.log("Checking password");
// Match password
bcrypt.compare(password, user.password, (err, isMatch) => {
if (err) throw err;
if (isMatch) {
return done(null, user);
} else {
return done(null, false, { message: "Password incorrect" });
}
});
});
})
);
passport.serializeUser(function (user, done) {
done(null, user.id);
});
passport.deserializeUser(function (id, done) {
User.findById(id, function (err, user) {
done(err, user);
});
});
};
I was able to fix this by chaning deserialize to:
passport.deserializeUser(function (id, done) {
User.findById(id)
.lean()
.exec(function (err, user) {
done(err, user);
});
});

How do I logout with JWT, passport Authentication (NodeJS)?

I am new to NodeJS and I have been following a tutorial. I made some changes to the tutorial code in order to allow users to login by typing in their username or email instead of just username.
passport.use(
new LocalStrategy(
(username_or_email, password, done) => {
User.findOne({ email: username_or_email }, (err, user) => {
if (err) {
return done(err);
console.log(err);
}
if (!user) {
User.findOne({ username: username_or_email }, (err, user) => {
//If there is an error
if (err) {
return done(err);
console.log(err);
}
//If there is no user
if (!user) {
return done(null, false);
} else {
user.comparePassword(password, done);
}
});
} else {
user.comparePassword(password, done);
}
});
}
)
The code above works well and allows users to type in username OR password to login.
Now when I follow the tutorial for how to logout, their method doesn't work for me.
I have a route like this, it is supposed to log a user out.
userRouter.get(
'/logout',
passport.authenticate('jwt', { session: false }),
(req, res) => {
res.clearCookie('access_token');
res.json({ user: { username: '', role: '' }, success: true });
}
);
When I go to it in Postman it says "Unauthorized" and does not return anything else.
I believe it could be something to do with my 'jwt' set up, shown below.
passport.use(
new JwtStrategy(
{
jwtFromRequest: cookieExtractor,
secretOrKey: 'NoobCoder',
},
(payload, done) => {
console.log(payload);
User.findById({ _id: payload.sub }, (err, user) => {
if (err) {
return done(err, false);
console.log('1');
}
if (user) {
return done(null, user);
console.log('2');
} else {
return done(null, false);
console.log('3');
}
});
}
)
);
This is the cookieExtractor function that I use for jwtFromRequest
const cookieExtractor = (req) => {
let token = null;
console.log(token);
if (req && req.cookies) {
token = req.cookies['access_token'];
}
return token;
};
The only console output I get is the console.log in the cookieExtractor.
Which makes me believe that that must be the point of failure. It is a "null" output as expected, and if I console.log the token, I get the current logged in users token. I believe the jwtFromRequest calls the cookieExtractor function but fails at some point soon after.

passport-jwt returns unauthorized when I go to /admin page on my web app

I am trying to get the user logged in using passport and jwt token. I manage to generate jwt token successfully but when I go to /admin page by typing in the browser I am getting the unauthorized message. I have read all the answer here but they aren't helping me out.
My passport.js file
try {
passport.use('signin', new LocalStrategy(
function(username, password, done) {
Users.findOne({username: username}, (err, user) => {
if (err) { return(err)}
if(!user) { return done(null, false, { message: 'Incorrect username.' })}
if (!checkUser(password, user.password)) { return done(null, false, { message: 'Incorrect username.' }); }
return done(null, user);
});
}
))
} catch (error) {
return done(error);
}
passport.use(new JWTstrategy({
secretOrKey: config.get('jwt.tokenKey'),
jwtFromRequest: ExtractJWT.fromAuthHeaderAsBearerToken()
}, async(token, done) => {
try {
return done(null, token.user);
} catch (err){
done(err);
}
}))
My login-controller file: -
router.post('/', (req, res) => {
passport.authenticate('signin', {session:false}, async(err,user,info) => {
try {
if (err || !user) {
console.log(err);
}
req.login(user, {session:false}, err => {
if (err) res.send(err);
const body = { username: user.username};
const token = jwt.sign( {user:body}, config.get('jwt.tokenKey'));
res.json({ success: true,
token: 'Bearer ' + token });
});
} catch (error) {
return next(error);
}
})(req, res);
})
My admin page file: -
router.get('/', passport.authenticate('jwt', { session: false }), function (req,res) {
res.render('./../views/admin/admin.pug');
})

Passport authentication ReferenceError: user is not defined

I am trying to make an authentication system for my website.
But when trying to log in with any combination I am receiving the following error in the website:
ReferenceError: user is not defined
user is not defined
ReferenceError: user is not defined
at Strategy._verify (/home/jarno/0__projects/nodejs/EasyOrders/routes/users.js:76:36)
at Strategy.authenticate (/home/jarno/0__projects/nodejs/EasyOrders/node_modules/passport-local/lib/strategy.js:90:12)
at attempt (/home/jarno/0__projects/nodejs/EasyOrders/node_modules...
In the console:
POST /users/login 500 204.677 ms - 3063
events.js:160
throw er; // Unhandled 'error' event
^
Error: Can't set headers after they are sent.
at ServerResponse.OutgoingMessage.setHeader (_http_outgoing.js:356:11)
at ServerResponse.header (/home/jarno/0__projects/nodejs/EasyOrders/node_modules/express/lib/response.js:719:10)...
My code:
Users.js
passport.use(new LocalStrategy({
usernameField: 'email'
},
function(email, password, done) {
User.getUserByEmail(email, function(err, user) {
if(err) throw err;
if(!user) {
return done(null, false, {message: 'Unknown User'});
}
});
User.comparePassword(password, user.password, function(err, isMatch) {
if(err) throw err;
if(isMatch) {
return done(null, user);
} else {
return done(null, false, {message: 'Invalid password'});
}
});
}
));
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
User.getUserById(id, function(err, user) {
done(err, user);
});
});
router.post('/login',
passport.authenticate('local', {successRedirect: '/', failureRedirect:'/users/login', failureFlash: true}),
function(req, res) {
res.redirect('/');
});
module.exports = router;
User.js model:
var UserSchema = mongoose.Schema({
email: {
type: String,
index: true
},
name: {
type: String
},
password: {
type: String
},
});
var User = module.exports = mongoose.model('User', UserSchema);
module.exports.createUser = function(newUser, callback) {
bcrypt.genSalt(10, function(err, salt) {
bcrypt.hash(newUser.password, salt, function(err, hash) {
newUser.password = hash;
newUser.save(callback);
});
});
}
module.exports.getUserByEmail = function(email, callback) {
var query = {email: email};
User.findOne(query, callback);
}
module.exports.getUserById = function(id, callback) {
User.findById(id, callback);
}
module.exports.comparePassword = function(candidatePassword, hash, callback) {
bcrypt.compare(candidatePassword, hash, function(err, isMatch) {
if(err) throw err;
callback(null, isMatch);
});
}
The problem is here:
User.getUserByEmail(email, function(err, user) {
if(err) throw err;
if(!user) {
return done(null, false, {message: 'Unknown User'});
}
});
User.comparePassword(password, user.password, function(err, isMatch) {
if(err) throw err;
if(isMatch) {
return done(null, user);
} else {
return done(null, false, {message: 'Invalid password'});
}
});
You are trying to use user.password when it's not already defined.
You need to nest the User.comparePassword function inside User.getUserByEmail or properly resolve it using promises.
Well the prompted message show that the user is not defined. It's the message error throwns in users.js .I think, You forgot to mention the name of your passport strategie that you have defined. Your code in users.js should be like this :
passport.use('local', new LocalStrategy({.......})

Node.js bcrypt compare function always returns false

I need a way to compare two hashed passwords before performing a post request in node js. The hashed password is stored in a mongoDB database. I've narrowed my mistake down to these possible areas.
//Part 1
router.post('/signIn', function (req, res) {
if(errors){
console.log(errors);
res.render('signIn', {
errors: errors
});
}else {
module.exports = function (passport) {
passport.authenticate('local',{successRedirect: '/dashboard', failureRedirect: '/', failureFlash: true})
},
module.exports = function (passport) {
passport.use(new LocalStrategy(
function (username, password, done) {
User.getUserByUsername(inputUser, function (err, user) {
if (err) throw err;
if (!user) {
req.flash('error_msg','Unknown Username');
return done(null, false);
}
User.comparePassword(inputPwd, user, hash, function (err, isMatch) {
if (err) throw err;
if (isMatch) {
req.flash('error_msg','Unknown Password');
return done(null, user);
} else {
return done(null, false);
}
})
})
}))
},
module.exports = function (passport) {
passport.serializeUser( function (user, done) {
done(null, user.id());
});
},
module.exports = function (passport) {
passport.deserializeUser( function (id, done) {
User.getUserById(id, function (err, user) {
done(err, user);
});
});
};
res.redirect('/users/dashboard');
}
});
These parts are from different files.
//Part 2:
module.exports.createUser = function (newUser, callback) {
bcrypt.hash(newUser.password, 10, function(err, hash) {
newUser.password = hash;
newUser.save(callback);
});
}
module.exports.getUserByUsername = function (username, callback) {
var query = {username: username};
User.findOne(query, callback);
}
module.exports.comparePassword = function (password, hash, callback ) {
bcrypt.compare(password, hash, function(err, isMatch) {
if (err) throw err;
callback(null, isMatch);
console.log(isMatch);
});
}
Ok, you have made quite some mistakes, i hope i found all. I will go through them via comments in your code:
function (username, password, done) {
// inputUser is undefined, shouldn't that be username?
User.getUserByUsername(inputUser, function (err, user) {
if (err) throw err; // you should never throw in async callbacks! use done(err) instead
if (!user) {
req.flash('error_msg','Unknown Username');
return done(null, false);
}
// all your input arguments are undefined! Where does inputPwd, user and has coe from?
// inputPassword should be password i guess, hash idk, maybe user.hash?
// also your arguments are wrong
User.comparePassword(inputPwd, user, hash, function (err, isMatch) {
if (err) throw err; // again, don't throw!
if (isMatch) {
req.flash('error_msg','Unknown Password');
return done(null, user);
} else {
return done(null, false);
}
})
})
}));
Now a corrected version:
function (username, password, done) {
User.getUserByUsername(username, function (err, user) {
if (err) return done(err);
if (!user) {
req.flash('error_msg','Unknown Username');
return done(null, false);
}
User.comparePassword(password, user.password, function (err, isMatch) {
if (err) return done(err);
if (isMatch) {
req.flash('error_msg','Unknown Password');
return done(null, user);
} else {
return done(null, false);
}
})
})
}));

Categories