Паспорт: разрешить подписку на имя и адрес электронной почты? (Локальная стратегия)

Есть ли способ разрешить пользователю регистрироваться в локальной стратегии с его паролем, электронной почтой и именем?
В каждом примере, который я мог найти в Интернете, используйте только имя/пароль или адрес электронной почты/пароль.

Я также просмотрел всю документацию паспорта, но эта документация вообще не полезна. Это просто один раздутый сайт, полный примеров.
Мне просто нужен список функций, классов и переменных с использованием паспортов с объяснениями, что они и каждый параметр из них делают. У каждой хорошей библиотеки есть что-то в этом роде, почему я не могу найти ее для паспорта?

Вот ключевые части моего кода:

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"
}));

Итак, действительно ли паспорт ограничен? Должен быть способ сделать это, правильно?

Ответ 1

Вы можете быть немного смущены, но паспорт не реализует методы регистрации. Это просто библиотека авторизации. Таким образом, вы должны использовать этот прецедент самостоятельно.

Прежде всего, создайте маршрут, который будет отвечать за регистрацию и ваши проверки:

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);
}

Приведенный выше пример основан на структуре Sails, но вы можете поместить его без проблем в свой собственный случай.

Следующий шаг включает в себя локальную стратегию паспорта.

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));

Теперь у нас есть только задача подписи. Это просто.

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);
}

Этот путь более подходит для паспорта и отлично подходит для меня.

Ответ 2

Вот то, что сработало для меня, решение основано на odm на основе мангуста, первая часть - часть, относящаяся к паспорту, я также привязал часть пользователя от odm к тому, как делается шифрование пароля.

Если я понял ваш вопрос, вы хотите, чтобы пользователь набрал либо свой адрес электронной почты, либо пароль. В этом случае измените поиск, чтобы попробовать оба, то есть соответствовать предоставленному идентификатору пользователя (в вашем вызове findOne (...) с именем пользователя или паролем.

Обратите внимание, что я использую bcrypt, чтобы избежать сохранения четких паролей, поэтому существует специальный метод сравнения для проверки паролей. Также обратите внимание на "подсказки" об использовании google auth, а также в моей системе, если это актуально, сообщите мне, и я могу добавить требуемый код.

------------ Auth part (только соответствующие фрагменты) -----------

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');
        }
    });
});

--- пользователь /odm, соответствующие части ----------------

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);
    });
};

Ответ 3

Скажем, у вас есть это

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');
    });

Обратите внимание, что у .authenticate есть явный тег.

Теги это 'my-simple-login-strategy'

Это означает, что у вас есть это...

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)
           })
        }
    )
)

!!! !!! ЗАМЕТЬТЕ, ЧТО "ЛОКАЛЬНЫЙ" - ТОЛЬКО НАИМЕНОВАНИЕ ТЕГОВОГО ДИСКА !!! !!!

В passport.use мы всегда вставляем явный тег. Это намного понятнее, если вы сделаете это. При использовании стратегии вставьте явный тег в стратегию и в app.post.

Так что моя-простая-логин-стратегия.

Какова действительная функция db.findUserByEmailPass sql?

Мы вернемся к этому!

Итак, у нас есть стратегия my-simple-login

Далее...... нам нужна моя-простая-createaccount-стратегия

Обратите внимание, что мы все еще хитро используем passport.authenticate:

Итак:

стратегия my-simple-createaccount-стратегии фактически создаст аккаунт.

Однако.............

вы все равно должны вернуть структуру.

Обратите внимание, что my-simple-login-стратегии должна возвращать структуру.

Итак, my-simple-createaccount-стратегии также должна возвращать структуру - точно так же.

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');
    });

А вот и стратегия..........

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)
            })
        }
    )
)

Стратегия почти такая же. Но вызов БД отличается.

Итак, теперь давайте посмотрим на звонки БД.

Давайте посмотрим на звонки в БД!

Обычный вызов БД для обычной стратегии будет выглядеть следующим образом:

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)
        })
}

Так что export.findUserByEmailPass, который используется моей-простой-логин-стратегией.

Но как насчет exports.simpleCreate для my-simple-createaccount-стратегии?

Простая версия игрушки будет

  1. проверьте, существует ли уже имя пользователя - верните false на этом этапе, если оно уже существует, тогда
  2. создайте его, а затем
  3. на самом деле просто верните запись снова.

Напомним, что (3) аналогичен обычному вызову "find".

Помните... стратегия my-simple-createaccount-стратегии на самом деле делает аккаунтом. Но вы все равно должны вернуть структуру таким же образом, что и ваша обычная стратегия аутентификации, my-simple-login-стратегии.

Таким образом, export.simpleCreate представляет собой простую цепочку из трех вызовов:

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)
        })
}

И это все работает.

Но обратите внимание, что

Паспорт не имеет ничего общего с созданием аккаунта!

На самом деле вам вовсе не обязательно использовать стратегию.

В app.post('/createaccount' вы можете, если хотите, ничего не делать с passport.authenticate... даже не упоминайте об этом в коде. Не используйте аутентификацию вообще. Просто сделайте sql-процесс вставки нового пользователя прямо в app.post.

Однако, если вы "хитро" используете паспортную стратегию - в моем примере my-simple-createaccount-стратегии - у вас есть бонус, что пользователь сразу же входит в систему с сеансом, и все работает по тому же шаблону, что и сообщение входа в систему., Здорово.

Ответ 4

На самом деле это не имеет ничего общего с passport и довольно просто, если вы используете body-parser. Убедитесь, что в вашей форме есть input field с атрибутом name="name", где вы регистрируете имя пользователя, например:

<div class="form-group">
    <label for="signup-name">Name</label>
    <input type="text" placeholder="Name" name="name">
</div> 

В вашей маршрутизации вы можете получить доступ к этому полю с помощью 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;

}));

Таким образом, вы можете добавить столько полей ввода формы, сколько захотите, и обращаться к ним по значению атрибута name. Второй пример:

<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;

Ответ 5

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);
             });
        });
    }));    
}

Ответ 6

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                                                                     
       }                                                                          
    }                                                                        
})