Check if user exists in passport-local - node.js

I have an Express app with the passport-local strategy, using Mongoose for storing user Accounts. I had a freelancer write this part of the app for me because I couldn't make sense of how to build the login system from beginning to end (every single tutorial I found did it in a different way). The drawback of this is that I don't understand what each part does. This is in my app.js file:
const Account = require('./models/db/AccountsSchema');
app.use(passport.initialize());
app.use(passport.session());
passport.use(new LocalStrategy(Account.authenticate()));
passport.serializeUser(Account.serializeUser());
passport.deserializeUser(Account.deserializeUser());
and this is in routes/index.js:
router.post('/register', function(req, res) {
Account.register(new Account({
username: req.body.username,
name: req.body.name
}), req.body.password, function(err, account) {
if (err) {
console.log(err);
} else {
console.log(account);
passport.authenticate('local', {
successRedirect: '/',
failureRedirect: '/login'
})(req, res, function(err, user) {
res.redirect('/');
});
}
});
});
along with:
router.post('/login',
passport.authenticate('local'),
function(req, res) {
res.redirect('/');
}
);
Now in the login POST request I want to have a check for whether a user with that particular username exists, so that if the password is wrong at least I can tell the user that the password is wrong. User enumeration is not a security concern here.
Where in my code can I incorporate a database check for whether an Account with the specified username exists?

In Account.authenticate() function. As it sets the LocalStrategy in your case.
setting your local strategy:
passport.use(new LocalStrategy(Account.authenticate()));
Sample Code:
function authenticate(username, password, done) {
User.findOne({ username: username }, 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);
});
}

Related

Passport login failing using email instead of username

I am using 'email' instead of 'username' for my passport local instance. Below is my login route. I also have included the part of my user model where I set username to be email. I am sending the exact same login data used to create the user.
Login route
router.post("/login", passport.authenticate("local", {
successRedirect: "/",
failureRedirect: "/login"
}), function(req, res) {});
Added to user model
UserSchema.plugin(passportLocalMongoose, { usernameField : 'email' });
if you using local strategy, you need to define passport strategy variables. see docs http://www.passportjs.org/docs/username-password/
add new config file for passport
var passport = require('passport')
, LocalStrategy = require('passport-local').Strategy;
passport.use(new LocalStrategy(
function(username, password, done) {
User.findOne({ email: username }, 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);
});
}
));
User is the schema of your mongoose. And replace username with email.

Github oAuth with PassportJS not authenticated

I'm trying to use passportJS to authenticate with the github-strategy. I've previously used passportJS with success, but not with the github-strategy, though I don't figure it should be much different. /viewData is the route in which I'm testing for authentication.
The strategy authenticates successfully and puts the data in the DB and when I serialize and deserialize the user data is in fact there. The problem comes when I try to check in any route after if the req.isAuthenticated() or even if req.session.passport.user exists and it returns false and undefined respectively. The weird thing the same check in app.js after I've redirected through /testAuth the authentication logs correctly with the true and id#. So I feel like its some issue with express-session or the passport integration with that that's breaking it, but I can't find it. I've been on this for a long time so hopefully someone can help me.
app.use(cookieParser('test'));
app.use(session({
secret: 'test',
resave: false,
saveUninitialized: true
}));
//pasport setup
app.use(passport.initialize());
app.use(passport.session());
// Passport init
passport.use(new GithubStrategy({
clientID: config.github.clientID,
clientSecret: config.github.clientSecret,
callbackURL: config.github.callbackURL },
function(accessToken, refreshToken, profile, done) {
User.findOne({ oauthID: profile.id }, function(err, user) {
if(err) {
console.log(err); // handle errors!
}
if (!err && user !== null) {
done(null, user);
} else {
user = new User({
oauthID: profile.id,
name: profile.displayName,
created: Date.now()
});
user.save(function(err) {
if(err) {
console.log(err); // handle errors!
} else {
console.log("saving user ...");
done(null, user);
}
});
}
});
}
));
// test authentication
function ensureAuthenticated(req, res, next) {
if (req.isAuthenticated()) { return next(); }
console.log("Not Authenticated", req.user);
res.redirect('/');
}
app.get('/testAuth', ensureAuthenticated, function(req, res){
User.findById(req.session.passport.user, function(err, user) {
if(err) {
console.log(err); // handle errors
} else {
res.redirect('/viewData');
}
});
});
//auth pages
app.get('/auth/github',
passport.authenticate('github'),
function(req, res){});
app.get('/auth/github/callback',
passport.authenticate('github', { failureRedirect: '/' }),
function(req, res) {
res.redirect('/testAuth');
});
// serialize and deserialize for session
passport.serializeUser(function(user, done) {
console.log('serializeUser: ' + user);
done(null, user._id);
});
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user){
console.log('deserializeUser: ' + user);
if(!err) done(null, user);
else done(err, null);
});
});
So this is a weird one, but I was fixing something else that was weird and in doing so solved this problem. I just moved my
app.use('/', index);
app.use('/viewData', viewData);
To below all the passport code and it works now.

Passport.js multiple local strategies and req.user

The client for my web-app wants to totally separate regular users and admins, hence I'm trying to implement two local strategies for passport.js:
passport.use('local', 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: 'Wrong email or password.' });
if (!user.validPassword(password)) return done(null, false, { message: 'Wrong email or password.' });
done(null, user);
});
}));
passport.use('admin', new LocalStrategy({
usernameField: 'email'
}, function(email, password, done) {
Admin.findOne({ email: email }, function(err, admin) {
if (err) return done(err);
if (!admin) return done(null, false, { message: 'Wrong email or password.' });
if (!admin.validPassword(password)) return done(null, false, { message: 'Wrong email or password.' });
done(null, admin);
});
}));
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
Admin.findById(id, function(err, admin) {
if (err) return done(err);
if (admin) return done(null, admin);
User.findById(id, function(err, user) {
done(err, user);
});
});
});
And then in my API admin router:
router.post('/', function(req, res, next) {
if (typeof req.body.email === 'undefined') return res.json({ success: false, message: 'Email not supplied.' });
if (!validator.isEmail(req.body.email)) return res.json({ success: false, message: 'Wrong email format.' });
if (typeof req.body.password === 'undefined') return res.json({ success: false, message: 'Password not supplied.' });
if (req.body.password.length === 0) return res.json({ success: false, message: 'Password not supplied.' });
passport.authenticate('admin', function(err, admin, info) {
if (!admin) return res.json({ success: false, message: info.message });
req.logIn(admin, function(err) {
if (err) return res.json({ success: false, message: 'Server error.', reason: err });
console.log('user:');
console.log(req.user); // both users and admins go here
console.log('admin:');
console.log(req.admin); // undefined
res.json({ success: true });
});
})(req, res, next);
});
In fact, I was following the answer here: https://stackoverflow.com/a/21898892/1830420, and Matthew Payne gets two session variables: req.user and req.sponsor. In my case, even if an admin authenticates, it gets written to req.user. Now, trying to implement my client's desire of totally separating users and admins, I want to get req.user and req.admin, but every time it gets written to req.user. I thought changing LocalStrategy name would help, but passport.js seems to ignore both the strategy name and model name.
Any help is very much appreciated.
P.S. I know I can have everyone in User model and write some middleware to protect certain routes based on roles, but then again, unfortunately, I don't get to choose here.
Unfortunately you can't do that with Passport. You will need to create express middleware to handle admin authentication.
And like Hya answered passport dont are good for authorization ...
You need to use middlewares for set admin context and admin roles:
// add this middlewares after your routes...
// set admin context and others things like admin templates
app.use('/admin/*', function adminContext(req, res, next) {
// set admin context
req.isAdmin = true;
next();
});
// then get roles for authenticated user in your passport stategy:
app.use(function getUserRoles(req, res, next) {
req.userRoleNames = [];
if (req.isAuthenticated()) {
req.userRoleNames.push('authenticated');
} else {
req.userRoleNames.push('unAuthenticated');
return next(); // skip role load if dont are authenticated
}
// get user roles, you may get roles from DB ...
// and if are admin add its role
req.userRoleNames.push('administrator');
next();
});
// and check roles in admin page
app.get('/admin/admin-page/', function (req, res, next) {
if (req.userRoleNames.indexOf('administrator') == -1) return res.status(403).send('forbidden');
console.log('roles:', req.userRoleNames);
res.status(200).send('Hey you are the administrator!');
});
See we.js core for see one advanced roles and permissions implementation: https://github.com/wejs/we-core

how to specify multiple strategy in passport

I am using passport local for authenticating the application .I understood to perform login
Now i wanted to implement registration for the same
I understood we can provide a name to distinguish both local strategy
but i don't know how to invoke them
Login:
app.post('/login',
passport.authenticate('local', {
successRedirect: '/loginSuccess',
failureRedirect: '/loginFailure',
failureFlash: true
}));
passport.use('login',new LocalStrategy(
function(username, password, done) {
process.nextTick(function () {
});
UserDetails.findOne({'username':username},
function(err, user) {
if (!user) { return done(null, false , { message: 'Incorrect username.' }); }
if (err) { return done(err); }
if (user.password != password) { return done(null, false , { message: 'Incorrect password.' }); }
return done(null, user);
});
}
));
app.post('/register',
passport.authenticate('local', {
successRedirect: '/loginSuccess',
failureRedirect: '/loginFailure',
failureFlash: true
}));
passport.use('register',new LocalStrategy(
function(username, password, done) {
process.nextTick(function () {
});
UserDetails.findOne({'username':username},
function(err, user) {
if (user) { return done(null, false , { message: 'EmailId Already exits' }); }
if (err) { return done(err); }
return done(null, user);
});
}
));
where should i configure login and register to pick up corresponding strategy accordingly i am a new bee please revert if someone not understood my question
Updating as cant put code in comment:
Please go through with this article https://scotch.io/tutorials/easy-node-authentication-setup-and-local
and let us know if you face any issue.
You can define it in your end points ..see below.. here /login is your end point where you will receive your requests and in function do your coding for checking user, etc and send res.json with response to user.
app.post('/login',
passport.authenticate('local', { failureRedirect: '/login' }),
function(req, res) {
res.redirect('/');
});

Verify access/group in Passport.js

I would like to use passport.js to verify that when users hit certain endpoints that they not only have the correct password but are a member of a specific group or have a certain access.
For simplicity sake if I have access levels of USER and ADMIN.
I can use passport to authenticate a password:
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: 'Incorrect username.' });
}
if (!user.validPassword(password)) {
return done(null, false, { message: 'Incorrect password.' });
}
return done(null, user);
});
}
));
Then with a route I can make sure the user passes auth:
app.get('/api/users',
passport.authenticate('local'),
function(req, res) {
res.json({ ... });
});
But lets say you need to have ADMIN acess to hit /api/users'. Do I need to write my own Strategies? IE do I need to have a local-user, and local-admin strategy, and in each verify the proper access levels?
I think I can do this pretty easily, but the problem arises when I need my site to have different auth methods (maybe sometimes use oauth), I would need to write custom *-user, *-admin strategies for each. Seems like overkill.
Other option is to just verify access/group in each route after the user has been authenticated. But I would prefer to do this in the middle-ware if possible.
Thanks
You could create a simple middleware that checks the group:
var needsGroup = function(group) {
return function(req, res, next) {
if (req.user && req.user.group === group)
next();
else
res.send(401, 'Unauthorized');
};
};
app.get('/api/users',
passport.authenticate('local'),
needsGroup('admin'),
function(req, res) {
...
});
This assumes that the object stored in req.user has a property group. This object is the one passed along from the strategy implementation and deserializeUser.
An alternative could be connect-roles, but I don't know how well that integrates with Passport.
EDIT: you could also combine Passport and the group-checking middleware:
var needsGroup = function(group) {
return [
passport.authenticate('local'),
function(req, res, next) {
if (req.user && req.user.group === group)
next();
else
res.send(401, 'Unauthorized');
}
];
};
app.get('/api/users', needsGroup('admin'), function(req, res) {
});

Resources