Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Node.js Passport strategy login with either Email or Username

I am using Passport on my node.js app and I am currently using an Username to login.

On my user register page, I have allow the user to register their unique username and email.

I want a login page with "Sign in Using username/email:" ________

Where the script can detect if there is a "@" in the field and look up the email instead of username.

I have tried for a few hours with no avail.

here is my passport.js

var mongoose = require('mongoose')
var LocalStrategy = require('passport-local').Strategy

var User = mongoose.model('User');

module.exports = function(passport, config){

passport.serializeUser(function(user, done){
    done(null, user.id);
})

passport.deserializeUser(function(id, done) {
    User.findOne({ _id: id }, function (err, user) {
        done(err, user);
    });
});

passport.use(new LocalStrategy({
    usernameField: 'username',
    passwordField: 'password'
    }, function(username, password, done) {
    User.isValidUserPassword(username, password, done);
    }));
}

EDIT: below is the user.js as requested

var mongoose = require('mongoose');
var hash = require('../util/hash.js');

UserSchema = mongoose.Schema({
username:  String,
email:      String,
salt:       String,
hash:       String
})

UserSchema.statics.signup = function(username, email, password, done){
var User = this;

hash(password, function(err, salt, hash){
    if(err) throw err;
    // if (err) return done(err);
    User.create({
        username : username,
        email: email,
        salt : salt,
        hash : hash
    }, function(err, user){
        if(err) throw err;
        // if (err) return done(err);
        done(null, user);
    });
});
}

UserSchema.statics.isValidUserPassword = function(username, password, done) {
this.findOne({username : username}, function(err, user){
    // if(err) throw err;
    if(err) return done(err);
    if(!user) return done(null, false, { message : 'Incorrect username.' });
    hash(password, user.salt, function(err, hash){
        if(err) return done(err);
        if(hash == user.hash) return done(null, user);
        done(null, false, {
            message : 'Incorrect password'
        });
    });
});
};

var User = mongoose.model("User", UserSchema);

module.exports = User;
like image 525
user3081516 Avatar asked Jan 06 '14 19:01

user3081516


4 Answers

Ok, you should have something like this in your Mongoose model:

UserSchema.statics.isValidUserPassword = function(username, password, done) {
    var criteria = (username.indexOf('@') === -1) ? {username: username} : {email: username};
    this.findOne(criteria, function(err, user){
        // All the same...
    });
};
like image 87
bredikhin Avatar answered Nov 01 '22 08:11

bredikhin


its a mongoose thing rather than a passport thing, and if you can try out this, using bredikhin's answer ^_^ :

var criteria = {$or: [{username: username}, {email: username}, {mobile: username}, {anything: username}]};

The key is to find a user through a query from mongodb!!!
In this way, you can have whatever query you want to find a user.

like image 24
alexgzhou Avatar answered Nov 01 '22 08:11

alexgzhou


For sign ins with username OR email, you could also use passport-local-mongoose's findByUsername option modify the queryParameters

// add passport functions
// schema.plugin(passportLocalMongoose);
schema.plugin(passportLocalMongoose, {
    // allow sign in with username OR email
    findByUsername: function(model, queryParameters) {
        // start
        // // queryParameters => { '$or' : [ { 'username' : 'searchString' } ] }
        // iterate through queryParameters
        for( let param of queryParameters.$or ){
            // if there is a username
            if( typeof param == "object" && param.hasOwnProperty("username") ){
                // add it as an email parameter
                queryParameters.$or.push( { email : param.username } );
            }
        }
        // expected outcome
        // queryParameters => { '$or' : [ { 'username' : 'searchString' }, { 'email' : 'searchString' } ] }
        return model.findOne(queryParameters);
    }
});
like image 1
Chris Fust Avatar answered Nov 01 '22 07:11

Chris Fust


its very simple you have to redefine the passport strategy your self, just like in the code below,

serialize with username

passport.serializeUser(function(user, done) {
    done(null, user.username);});

deserialize with username

passport.deserializeUser(function(username, done) {
User.findOne({username:username},function(err, user){
    done(err,user);
}); });

Passport Strategy

//passport strategy
passport.use(new LocalStrategy(function(username, password, done) {
        console.log(username.includes("@"));
      User.findOne((username.includes("@"))?{email:username}:{username:username}, function(err, user) {   
        if (err) {return done(err); }
        if (!user) {console.log("i am ERROR"); return done(null, false, { message: 'Incorrect username.' });}
        if (user.password===password) {return done(null, user); }
        return done(null, false);
      });
    }
  ));

NOTE: Here user.password===password means that the password in database is stored as plaintext.. And you have to insert password manually to database such as password : req.body.password also you have to apply encryption and decryption user self before adding or in comparing.

like image 1
lost veteran Avatar answered Nov 01 '22 08:11

lost veteran