Where does the var User come from in Passport documentation - javascript

I am just getting my head round Passport in Node
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);
});
}
));
This is all great, I understand the logic. However I don't understand where variables User from
User.findOne({ username:....
Is coming from? This isn't set anywhere and baffles me a little.

The example is using a mongoose model (User) that was defined elsewhere. Mongoose models have a findOne function. It's meant to mostly be filler to show how you would use the strategy to lookup info in the db to authenticate the user.

Related

Passport.js BasicStratagy retuns unauthorized to logged in user in a Rest API

I am working on a Rest API with Passport.js. after I log in and use a secured endpoint, I get "unauthorized" as a response. what is more confusing is that after I call the log in function, I get a valid user. here is Passport config:
passport.use(new BasicStrategy({
usernameField: 'email',
passwordField: 'password'
},
function(email, password, done) {
console.log("Email: ", email)
console.log("password: ", password)
User.findOne({ email: email }, function (err, user) {
if (err) { return done(err) }
if (!user) { return done(null, false) }
if (!bcrypt.compare(password, user.password)) { return done(null, false) }
return done(null, user);
})
}
))
I used the following middle ware code to secure it: passport.authenticate('basic', {session: false})

How do I determine a user is logged out when using passport?

Started learning Node today and I am almost positive my user is not being logged out correctly and I do not understand why. To login the user I've written the following code in my routes file
app.post('/api/signup', passport.authenticate('local-signup', function(err, res, req) {
console.log(res);
}));
then in another file this code to handle the logic of logging in my user
passport.serializeUser(function(user, done) {
console.log("serializeUser");
console.log(user);
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
console.log("deserializeUser");
console.log(id);
User.findById(id, function(err, user) {
done(err, user);
});
});
passport.use('local-signup', new LocalStrategy(
{
usernameField: 'username',
passwordField: 'password',
passReqToCallback: true
},
function(req, username, password, done) {
console.log(req.sessionID);
process.nextTick(function() {
User.findOne({ 'local.username': username }, function(err, user) {
if (err) {
return done(err);
}
if (user) {
return done(null, false, req.flash('signupMessage', "Username already taken"));
} else {
let newUser = new User();
newUser.local.username = username;
newUser.local.password = newUser.generateHash(password);
newUser.save( function(saveErr) {
if (saveErr) {
throw saveErr;
}
return done(null, newUser);
});
}
});
});
})
);
Then to logout my user I wrote the following code in my routes file
app.get('/api/session', function(req, res) {
console.log(req.sessionID);
req.logOut();
console.log(req.sessionID);
});
A few things confuse me about this code.
To my understanding serializeUser is suppose to change the user.id to bits and store it in the cookies but the console.log never prints which leads me to think that this code never runs, why? I read that req.login() is implicitly called so I should not need to worry about it.
The console.log which is inside the callback of LocalStrategy prints a sessionID. When is this set if I haven't created my new user yet or the console.log mentioned in point 1 never runs?
Inside of logout I call req.logOut() with a console.log(req.sessionID) before and after req.logOut() and these both print the same thing. I'm assuming these sessionID's are what the cookies use to determine a session is valid, if so, does this mean the user never logs out as the sessionID is never changed?
Thanks

Passport LocalStrategy inconsistent class name

I'm creating a strategy object like this :
var strat = new LocalStrategy({
usernameField: 'email'
},
function(username, password, done) {
User.findOne({ email: username }, function (err, user) {
if (err) { return done(err); }
// Return if user not found in database
if (!user) {
return done(null, false, {
message: 'User not found'
});
}
// Return if password is wrong
if (!user.validPassword(password)) {
return done(null, false, {
message: 'Password is wrong'
});
}
// If credentials are correct, return the user object
return done(null, user);
});
}
);
In my debugger, I see that the strat object is an instance of Class "Strategy".
Shouldn't it be an instance of "LocalStrategy" since it was created via LocalStrategy conscructor ?
The following is an example piece of code of what is normally do to setup passportjs using local strategy
var LocalStrategy = require('passport-local').Strategy;
As you can see Strategy is the name of the exported symbol and the name of the strategy constructor. LocalStrategy is just the name of a local variable you are using.

Passport: Allow sign up with name and email address? (Local Strategy)

Is there any way to allow a user to register on the local strategy with his password, email and name?
Every example I could find online only use name/password or email/password.
I also searched through the the whole passport documentation, but that documentation isn't helpful at all. It's just one bloated site full of examples.
I just need an list of functions, classes and variables passport uses with explanations what they and every parameter of them do. Every good library has something like that, why can't I find it for passport?
Here are the key parts of my code:
passport.use('local-signup', new LocalStrategy({
usernameField: 'email',
passwordField: 'password',
//are there other options?
//emailField did not seem to do anything
passReqToCallback: true // allows us to pass in the req from our route (lets us check if a user is logged in or not)
},
function(req, email, password, done) {
//check if email not already in database
//create new user using "email" and "password"
//I want an additional parameter here "name"
}));
So is passport really that limited? There has to be a way to do this, right?
You can be a little confused but passport doesn't implement signup methods. It's just authorisation library. So you must handle that use-case on your own.
First of all, create route that will be responsible for sign-up and your checks:
signup: function (req, res) {
User
.findOne({
or: [{username: req.param('username')}, {email: req.param('email')}]
})
.then(function(user) {
if (user) return {message: 'User already exists'};
return User.create(req.allParams());
})
.then(res.ok)
.catch(res.negotiate);
}
The example above is based on Sails framework, but you can fit it with no problems to your own case.
Next step is include passport local strategy.
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
var LOCAL_STRATEGY_CONFIG = {
usernameField: 'email',
passwordField: 'password',
session: false,
passReqToCallback: true
};
function _onLocalStrategyAuth(req, email, password, next) {
User
.findOne(or: [{email: email}, {username: email}])
.then(function (user) {
if (!user) return next(null, null, {
code: 'E_USER_NOT_FOUND',
message: email + ' is not found',
status: 401
});
if (!HashService.bcrypt.compareSync(password, user.password)) return next(null, null, {
code: 'E_WRONG_PASSWORD',
message: 'Password is wrong',
status: 401
});
return next(null, user, {});
})
.catch(next);
}
passport.use(new LocalStrategy(LOCAL_STRATEGY_CONFIG), _onLocalStrategyAuth));
We have only signin task now. It's simple.
signin: function(req, res) {
passport.authenticate('local', function(error, user, info) {
if (error || !user) return res.negotiate(Object.assign(error, info));
return res.ok(user);
})(req, res);
}
This way is more suitable for passport and works great for me.
Say you have this
app.post('/login', urlencodedParser,
// so, user has been to /loginpage and clicked submit.
// /loginpage has a post form that goes to "/login".
// hence you arrive here.
passport.authenticate('my-simple-login-strategy', {
failureRedirect: '/loginagain'
}),
function(req, res) {
console.log("you are in ............")
res.redirect('/stuff');
});
Note that the .authenticate has an explicit tag.
The tags is 'my-simple-login-strategy'
That means you have this ...
passport.use(
'my-simple-login-strategy',
// !!!!!!!!!!!!!note!!!!!!!!!!, the DEFAULT there (if you have nothing)
// is 'local'. A good example of defaults being silly :/
new Strategy(
STRAT_CONFIG,
function(email, password, cb) {
// must return cb(null, false) or cb(null, the_user_struct) or cb(err)
db.findUserByEmailPass(email, password, function(err, userFoundByDB) {
if (err) { return cb(err); }
if (!userFoundByDB) { return cb(null, false); }
console.log('... ' + JSON.stringify(userFoundByDB) )
return cb(null, userFoundByDB)
})
}
)
)
!!! !!! NOTE THAT 'local' IS JUST THE DEFAULT TAG NAME !!! !!!
In passport.use, we always put in an explicit tag. It is much clearer if you do so. Put in an explicit tag in the strategy and in the app.post when you use the strategy.
So that's my-simple-login-strategy.
What is the actual db.findUserByEmailPass sql function?
We'll come back to that!
So we have my-simple-login-strategy
Next ...... we need my-simple-createaccount-strategy
Note that we are still sneakily using passport.authenticate:
So:
the strategy my-simple-createaccount-strategy will actually make an account.
However .............
you should still return a struct.
Note that my-simple-login-strategy has to return a struct.
So, my-simple-createaccount-strategy also has to return a struct - in exactly the same way.
app.post('/createaccount', urlencodedParser,
// so, user has been to /createanaccountform and clicked submit,
// that sends a post to /createaccount. So we are here:
passport.authenticate('my-simple-createaccount-strategy', {
failureRedirect: '/loginagain'
}),
function(req, res) {
console.log("you are in ............")
res.redirect('/stuff');
});
And here's the strategy ..........
passport.use(
'my-simple-createaccount-strategy',
new Strategy(
STRAT_CONFIG,
function(email, password, cb) {
// return cb(null, false), or cb(null, the_user_struct) or cb(err)
db.simpleCreate(email, password, function(err, trueOrFalse) {
if (err) { return cb(err); }
if (!trueOrFalse) { return cb(null, false); }
return cb(null, trueOrFalse)
})
}
)
)
The strategy is pretty much the same. But the db call is different.
So now let's look at the db calls.
Let's look at the db calls!
The ordinary db call for the ordinary strategy is going to look like this:
exports.findUserByEmailPass = function(email, password, cb) {
// return the struct or false via the callback
dc.query(
'select * from users where email = ? and password = ?',
[email, password],
(error, users, fields) => {
if (error) { throw error } // or something like cb(new Error('blah'));
cb(null, (users.length == 1) ? users[0] : false)
})
}
So that's exports.findUserByEmailPass, which is used by my-simple-login-strategy.
But what about exports.simpleCreate for my-simple-createaccount-strategy?
A simple toy version would
check if the username exists already - return false at this point if it does exist already, then
create it, and then
actually just return the record again.
Recall that (3) is just like in the ordinary "find" call.
Remember ... the strategy my-simple-createaccount-strategy will actually make an account. But you should still return a struct in the same way as your ordinary authenticate strategy, my-simple-login-strategy.
So exports.simpleCreate is a simple chain of three calls:
exports.simpleCreate = function(email, password, cb) {
// check if exists; insert; re-select and return it
dc.query(
'select * from users where email = ?', [email],
(error, users, fields) => {
if (error) { throw error } // or something like cb(new Error('blah'));
if (users.length > 0) {
return cb(null, false)
}
else {
return partTwo(email, password, cb)
}
})
}
partTwo = function(email, password, cb) {
dc.query(
'insert into users (email, password) values (?, ?)', [email, password],
(error, users, fields) => {
if (error) { throw error } // or something like cb(new Error('blah'));
partThree(email, password, cb)
})
}
partThree = function(email, password, cb) {
dc.query(
'select * from users where email = ? and password = ?', [email, password],
(error, users, fields) => {
if (error) { throw error } // or something like cb(new Error('blah'));
cb(null, (users.length == 1) ? users[0] : false)
})
}
And that all works.
But note that
passport has nothing to do with account creation!
In fact, you do not have to use a strategy at all.
In app.post('/createaccount' you can, if you wish, do nothing with passport.authenticate ... don't even mention it in the code. Don't use authenticate at all. Just go ahead and do the sql process of inserting a new user, right there in app.post.
However, if you "trickily" use a passport strategy - my-simple-createaccount-strategy in the example - you have the bonus that the user is then immediately logged-in with a session and everything works in the same pattern as the login post. Cool.
Here is what worked for me, the solution is based on a mongoose based odm, the first part is the passport related part, I also attached the user part from odm to who how the encryption of the password is done.
If I understood your question, you want the user to type either his email or password. In this case modify the search to try both, that is, match the provided user identifier (in your call to findOne(...) with either the username or password.
Note that I use bcrypt to avoid storing clear passwords, that's why there is a customized compare method for testing passwords. Also note 'hints' of using google auth as well, My system enabled both, if it is relevant, please lemme know and I can add the required code.
------------ Auth part (just relevant snippets) -----------
var passport = require('passport'),
LocalStrategy = require('passport-local').Strategy;
passport.serializeUser(function(user, done) {
// the values returned here will be used to deserializeUser
// this can be use for further logins
done(null, {username: user.username, _id: user.id, role: user.role});
});
passport.deserializeUser(function(user, done) {
done(null, user);
});
passport.use(new LocalStrategy(function(username, password, done){
odm.User.findOne({username: username, authType: 'direct'}, function(err, user){
if(err){
return done(err, false);
}
if(!user){
return done(null, false);
}
if(user.role === 'new'){
console.log('can not use new user!');
return done('user not activated yet, please contact admin', false);
}
user.comparePassword(password,function(err, isMatch){
if(err){
return done(err, false);
}
if(isMatch){
return done(null, user);//{username: username});
}
return done(null, false);
});
});
}));
app.post('/login', function(req, res, next){
passport.authenticate('local', {
failureRedirect: '/logout?status=login failed'
}, function(err, user, info){
if(err){
return next(err);
}
if(!user){
return res.redirect('/login');
}
req.logIn(user, function(err){
if (req.body.rememberme) {
req.session.cookie.maxAge = 30*24*60*60*1000 ;//Rememeber 'me' for 30 days
} else {
req.session.cookie.expires = false;
}
var redirect = req.param('redirect') || '/index';
res.redirect(redirect);
});
}
)(req, res, next);
}
);
app.post('/register',function(req, res){
var user = new odm.User({username: req.body.username, password: req.body.password, email: req.body.email, authType: 'direct'});
user.save(function(err, user){
if(err){
console.log('registration err: ' , err);
} else {
res.redirect('/list');
}
});
});
--- user/odm, relevant parts ----------------
var bcrypt = require('bcrypt-nodejs');
// --------------------- User ------------------------------------------ //
var userSchema = new Schema({
name: String,
email: String,
username: {type: String, required: true, unique: true},
password: String,
role: {type: String, required: true, enum: ['new', 'admin', 'user'], default: 'new'},
authType: {type: String, enum: ['google', 'direct'], required: true}
});
userSchema.pre('save', function (next) {
var user = this;
if (!user.isModified('password')) return next();
console.log('making hash...........');
bcrypt.genSalt(SALT_WORK_FACTOR, function (err, salt) {
if (err) return next(err);
bcrypt.hash(user.password, salt, null, function (err, hash) {
if (err) return next(err);
user.password = hash;
next();
});
});
});
userSchema.methods.comparePassword = function (candidatePassword, cb) {
bcrypt.compare(candidatePassword, this.password, function (err, isMatch) {
if (err) return cb(err);
cb(null, isMatch);
});
};
var localStrategy = require('passport-local').Strategy;
var User = require('../public/models/user');
module.exports = function(passport){
passport.serializeUser(function(user, done){
done(null, user.id);
});
passport.deserializeUser(function(id, done){
User.findById(id, function(err, user){
done(err, user);
});
});
passport.use('local-signup', new localStrategy({
usernameField: 'email',
passwordField: 'password',
passReqToCallback: true
},
function(req, email, password, done){
process.nextTick(function(){
User.findOne({'local.enroll': email}, function(err, user){
if(err)
return done(err);
if(user){
return done(null, false, req.flash('signupmessage', 'The email already taken'));
} else{
var newUser = new User();
newUser.local.enroll = email;
newUser.local.password = newUser.generateHash(password);
newUser.save(function(err){
if(err)
throw err
return done(null, newUser);
});
}
});
});
}));
passport.use('local-login', new localStrategy({
usernameField: 'email',
passwordField: 'password',
passReqToCallback: true
},
function(req, email, password, done){
process.nextTick(function(){
User.findOne({'local.enroll': email}, function(err, user){
if(err)
return done(err);
if(!user){
return done(null, false, req.flash('loginmessage', 'No user found'));
}
if(!user.validPassword(password)){
return done(null, false, req.flash('loginmessage', 'Invalid password'));
}
return done(null, user);
});
});
}));
}
This has actually nothing to do with passport and is pretty simple, assuming you are using body-parser. Make sure you have an input field in your form with the attribute name="name" where you register the user's name like:
<div class="form-group">
<label for="signup-name">Name</label>
<input type="text" placeholder="Name" name="name">
</div>
In your routing, you can access this field with req.body.name:
passport.use('local-signup', new LocalStrategy({
usernameField: 'email',
passwordField: 'password',
//are there other options?
//emailField did not seem to do anything
passReqToCallback: true
},
function(req, email, password, done) {
//check if email not already in database
//create new user using "email" and "password"
//I want an additional parameter here "name"
user.email = email;
user.password = password; // Do some hashing before storing
user.name = req.body.name;
}));
So you can add as many form input fields as you want, access them by the value of the name attribute. A Second example would be:
<input type="text" placeholder="City" name="city">
<input type="text" placeholder="Country" name="country">
// Access them by
user.city = req.body.city;
user.country = req.body.country;
UserModel.find({email: req.body.email}, function(err, user){
if(err){
res.redirect('/your sign up page');
} else {
if(user.length > 0){
res.redirect('/again your sign up page');
} else{
//YOUR REGISTRATION CODES HERE
}
}
})
In strategy options set the passReqToCallback:true and then add req as parameter into your callback function. Finally, read the extra information from req.body object for example req.body.firstName
const signup = new Strategy({
usernameField: "username",
passwordField: "password",
passReqToCallback:true
}, async (req, username, password, done) => {
try {
const user = User.create();
user.username = username;
user.password = password;
user.firstName = req.body.firstName;
user.lastName = req.body.lastName
await user.save()
return done(null, user);
} catch (error) {
return done(error, null);
}
});

passport js check for error in req

I am logging in a user with passport local:
// route to log in
app.post('/login', passport.authenticate('local-login'), function(req, res) {
res.send(req.user);
});
This will only work in the case of success, i.e. when req.user is defined. How do I check for an error in the request? Essentially I want to take the error that was pulled from my passport authentication code in the done() call and send that back to the client to display the proper error message:
passport.use('local-login', new LocalStrategy({
username : 'email',
password : 'password',
passReqToCallback : true
},
function(req, email, password, done) {
console.log('logging in user: ' + email);
// find a user whose email is the same as the forms email
// we are checking to see if the user trying to login already exists
Landlord.findOne({ 'local.email' : email }, function(err, user) {
if (err) return done(err);
if (!user) return done(null, false, { message: 'Incorrect username.' });
if (!user.validPassword(password)) {
console.log('wrong pw');
return done(null, false, { message: 'Incorrect password.' });
}
return done(null, user);
});
}));

Categories

Resources