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.
Related
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()
I am trying to authenticate multiple user through multiple strategies. I have 2 table first is User table and second is employee but it's working when i try to login it show the error
User not exist
when i use just single authentication strategy it's work but when i use multiple strategy it shows error
Auth.js
const LocalStrategy = require('passport-local').Strategy
const bcrypt = require('bcrypt')
const User = require('../models/authUserModel')
const Employee = require('../models/employeeModel')
module.exports = function (passport) {
passport.use("user-local",
new LocalStrategy({usernameField: "email"}, function (email, password, done) {
User.findOne({email: email})
.then(user => {
if (!user) {
return done(null, false, ({message: 'Email not exist'}))
}
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'}))
}
})
}).catch(err => console.log(err))
})
)
passport.use('local',
new LocalStrategy({usernameField:"email"}, function (email, password, done){
Employee.findOne({email:"email"})
.then(user=>{
if(!user){
return done(null, false,'employee not exist')
}
bcrypt.compare(password, user.password, (err, isMatch)=>{
if(isMatch){
return done(null, user)
}
else {
return done(null, false, 'password or email is incorrect')
}
})
}).catch(err=>console.log(err))
})
)
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user);
});
});
}
Login.js
let express = require('express');
let router = express.Router();
const passport = require('passport')
router.get('/login', function(req, res, next) {
res.render('login');
})
router.post('/login', function (req, res, next) {
passport.authenticate("user-local", function (err, user, info) {
if (err) {
return next(err)
}
if (!user) {
console.log('User not exist')
return res.render('login')
}
req.logIn(user, function (err) {
if (err) {
return next(err)
}
req.session.isLoggedIn = true
req.session.user = user
req.session.save(err => {
console.log(err)
if (req.isAuthenticated()) {
return res.redirect('/customerMenu')
}
console.log('user not exist')
return res.render('login')
})
})
})(req, res, next)
passport.authenticate('local', function (err, user, info){
if(err){
return next(err)
}
if(!user)
{
console.log("employee not exist")
return res.render('login')
}
req.logIn(user,function (err){
if(err){return next(err)}
req.session.isLoggedIn = true
req.session.user = user
req.session.save(err=>{
console.log(err)
if (req.isAuthenticated()) {
return res.redirect(200,'/employeeMenu')
}
console.log('user not exist')
return res.render('login')
})
})
})(req, res, next)
})
function isLoggedIn(req, res, next){
if(req.isAuthenticated()){
req.isLogged = true
return next();
}
else{
req.isLogged = false
return next()
}
}
module.exports = isLoggedIn
module.exports = router
I think you need to edit your .serializeUser() and .deserializeUser() functions so that passport knows what type of user you are trying to serialize and deserialize.
The way I have done it in the past is by using a SessionConstructor that puts both the user's id and the type of user they are inside an object. You can then use this in your deserialize function to figure out which usertype you are trying to sign in.
function SessionConstructor(userId, userGroup){
this.userId = userId;
this.userGroup = userGroup;
}
passport.serializeUser((user, done) => {
let userGroup = 'user';
if (//condition to tell if it is the other user type){
userGroup = 'employee';
};
let sessionConstructor = new SessionConstructor(user.id, userGroup)
done(null, sessionConstructor);
});
passport.deserializeUser((sessionConstructor, done) => {
if (sessionConstructor.userGroup == 'user'){
User.findById(sessionConstructor.userId).then(user => done(null, user));
} else if (sessionConstructor.userGroup == 'employee'){
Employee.findById(sessionConstructor.userId).then( employee=> done(null, employee));
}
});
You will however need to think of a condition for the if statement inside of .serializeUser() that will tell you which type of user it is. If you include a unique identifier inside of the user object that is sent to .serializeUser(), then you can check that to tell. For example have a key called employee inside your user object where the value is true if its an employee submitting it, and false otherwise. If you choose this implementation it would look something like this.
passport.serializeUser((user, done) => {
let userGroup = 'user';
if (user.employee){
userGroup = 'employee';
};
let sessionConstructor = new SessionConstructor(user.id, userGroup)
done(null, sessionConstructor);
});
Outisde of that I think the below code should work, but feel free to ask any questions about this method and I'll do my best to answer!
Here is a link to where I learned about how to do this.
I'm trying to deal with authentication sessions in Node.js, Express, Passport app.
I made lines of code to use express-session and it still can't auth even when I register new user.
Here is strategy.
// Local Strategy
passport.use(new LocalStrategy({ usernameField: 'email' }, function(username, password, done){
User.findOne({ 'email': username }, function(err, user){
if(err) throw err;
if(!user){
return done(null, false, {type: "danger", message: 'No user found'});
}
// Match Password
bcrypt.compare(password, user.password, function(err, isMatch){
if(err) throw err;
if(isMatch){
return done(null, user);
} else {
return done(null, false, {type: "danger", message: 'Wrong password'});
}
});
});
}));
Here are serializers.
passport.serializeUser(function(user, done) {
console.log(user.id);
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
console.log(user.id);
done(err, user);
});
});
Here is login and register route.
// Login page and form
router.get('/login', function(req, res) {
res.render('login');
});
router.post('/login', passport.authenticate('local',
{successRedirect: '/chat',
failureRedirect: '/login'}));
// Register page and form
router.get('/register', function(req, res) {
let errors = [];
res.render('register', { 'errors': '' });
});
router.post('/register', [
check('name').notEmpty().withMessage('Name field is empty'),
check('surname').notEmpty().withMessage('Surname field is empty'),
check('email').notEmpty().withMessage('E-mail is empty'),
check('password').notEmpty().withMessage('Password field is empty'),
check('password_confirm').notEmpty().withMessage('Password confirmation field is empty'),
check("password", "Passwords don't match")
.custom((value,{req}) => {
if (value !== req.body.password_confirm) {
throw new Error("Passwords don't match");
} else {
return value;
}
}),
], function(req, res) {
const { name, surname, email, password } = req.body;
let errors = validationResult(req);
console.log(errors.errors);
if(!errors){
res.render('register', { 'errors': errors.errors });
console.log('ebebe');
} else {
console.log('oooo');
let NewUser = new User ({
name, surname, email, password
});
bcrypt.genSalt(10, function(err, salt) {
bcrypt.hash(password, salt, function(err, hash) {
NewUser.password = hash;
NewUser.save();
});
});
res.redirect('/chat');
}
});
Here is protected route.
router.get('/chat', (req, res) => {
if(req.isAuthenticated()) {
res.send('definitely secure page');
console.log(req.user);
console.log(req.isAuthenticated());
} else {
res.send('ebebe');
console.log(req.user);
console.log(req.isAuthenticated());
}
});
How to make it work properly and what am I doing wrong?
Here is a way to do it. You can use something like jsonwebtoken in combination with express-session and write a middleware function to check if the token is valid and use it to protect the routes that you want to protect. Here are some snippets of code that I hope will help guide you in the right direction.
First you can write a function like this in your UserSchema so you can use it later to generate a jwt token when the user logs in
var jwt = require('jsonwebtoken');
UserSchema.methods.generateJWT = function() {
var today = new Date();
var exp = new Date(today);
exp.setDate(today.getDate() + 60);
return jwt.sign({
id: this._id,
username: this.username,
exp: parseInt(exp.getTime() / 1000),
}, secret);
};
then in the login route you can use it to generate a token.
router.post('/login', passport.authenticate('local',
failureRedirect: '/login'}), function(req, res) {
req.user.token = user.generateJWT();
req.session.token = req.user.token;
res.redirect('/dashboard')
});
and then you can write the middleware
function auth(req, res, next) {
//console.log(req.session.token)
if (req.session.token) {
const token = req.session.token
let decoded = jwt.verify(token, secret)
console.log(decoded)
User.findById(decoded.id, function(err, user) {
if (err || !user) {
return res.redirect('/')
}
//console.log(user)
res.locals.user = user
req.user = user
next()
})
} else {
return res.redirect('/')
}
}
and then you can protect your routes with it
router.get('/protected', auth, function(req, res) {
})
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);
});
};
I recently added authentication to my website using bcrypt. When authenticating, bcrypt compares the passwords.
If passwords match req.session is set as a cookie and written to mongodb using connect-mongodb-session. But when redirecting to /events the app crashes and gives me an error: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
I'm using handlebars to render my html code
auth route:
Router.get('/', (req, res) => {
res.status(200).render('login', {pageTitle: 'Log In'});
})
Router.post('/', (req, res) => {
//Authenticate user
const username = req.body.username;
const pass = req.body.password;
User.findOne({username: username}).then((user) => {
if (user) {
//Validate password
console.log(user);
console.log(user.password);
bcrypt.compare(pass, user.password).then((doMatch) => {
console.log(doMatch);
//Check if password match
if (doMatch) {
//To use session use express-session package
req.session.isLoggedIn = true;
req.session.user = user;
req.session.save((err) => {
console.log(err);
res.redirect('/');
});
return res.redirect('/events');
} else {
res.redirect('/');
}
}).catch((err) => {
console.log(err);
});
} else {
return res.redirect('/');
}
}).catch((err) => {
console.log(err);
});
});
events route:
Router.get('/events', (req, res) => {
if (req.session.isLoggedIn) {
Event.find({}, (err, events) => {
res.render('events', {
prods: events,
pageTitle: 'Events',
path: '/events',
hasProducts: events.length > 0
});
}).catch((err) => {
console.log(err);
});
} else {
console.log('User not authenticated');
res.status(401).send('User not authenticated');
}
});
These lines are the culprit.
req.session.save((err) => {
console.log(err);
res.redirect('/');
});
return res.redirect('/events');
Here you pass a callback into save and immediately redirect res.redirect('/events').
After some time when callback redirect res.redirect('/'); throws an error.