Как встроить bcrypt в вызов mongose ​​API? - PullRequest
0 голосов
/ 22 марта 2019

Настройка

Я делаю авторизацию веб-сайта и хочу внедрить в него лучшие практики, сохраняя при этом код чистым и читабельным. На данный момент у меня есть классический код, подобный этому:

let foundUser = await userModel.findOne({ email: recievedEmail });
if(!foundUser)
    error("not authorized!");
const isPasswordMatch = await bcrypt.compare(recievedPassword, foundUser.password);
if(!isPasswordMatch)
    error("not authorized!");
foundUser.update({ $set: { lastLogin: new Date() }, $push: { myEvents: authEvent } });
foundUser.save();
success("authorized OK!");

Тем временем я задал вопрос о лучшей команде mongoose для выполнения auth , и мы создали следующую команду «auth-check-and-update» в «atomic» Способ:

const foundUser = await userModel.findOneAndUpdate(
    { email: recievedEmail, password: recievedPassword },
    { $set: { lastLogin: new Date() }, $push: { myEvents: authEvent } }
);
if(foundUser)
    success("authorized OK!");
else
    error("not authorized!");

Идея здесь очевидна - если пользователь с соответствующим адресом электронной почты и паролем найден , то пользователь считается авторизованным , а его отметка времени последнего входа в систему обновляется (одновременно).


Задача

Чтобы объединить лучшие практики из двух вышеперечисленных, мне нужно как-то встроить bcrypt.compare() call в findOneAndUpdate() call. Это сложно сделать, потому что я не могу просто "сравнить хешированные пароли"; bcrypt просто отличается от простых хэшей (например, sha или md5): Из соображений безопасности каждый раз возвращает разные хэши . (Ответы в ссылке объясняют «почему и как»).


Решение Попытка

Я посмотрел на mongoose-bcrypt пакет: он использует Schema.pre() функциональность:

 schema.pre('update', preUpdate);
 schema.pre('findOneAndUpdate', preUpdate);

Чтобы понять, пожалуйста, взгляните на mongoose-bcrypt\index.js. Вы увидите, что preUpdate влияет только на создание нового пользователя (.. andUpdate часть), но не на фактическую проверку (findOne .. часть). Таким образом, этот плагин может подходить для реализации «регистрация пользователя» / «изменить пароль». Но он не может работать для авторизации предлагаемым способом.


Вопрос

Как бы вы "комбинировали" bcrypt.compare() и userModel.findOneAndUpdate() звонки при таких обстоятельствах?

1 Ответ

1 голос
/ 25 марта 2019

Как насчет пароля для сравнения в UserModel, как это

// method to compare password input to password saved in database
UserModel.methods.isValidPassword = async function(password){
    const user = this;
    const compare = await bcrypt.compare(password, user.password);
    return compare;
}

А внутри вашего аутентификации или паспорта (я использую паспорт) сделайте что-то вроде этого

passport.use(new LocalStrategy(
    (username, password, done) => {
        // change your query here with findOneAndUpdate
        User.findOne({ username: username }, (err, user) => {
            if (err) { return done(err); }
            if (!user) {

                return done(null, false, { message: 'Incorrect username.' });
            }
            if (!user.isValidPassword(password)) {

                return done(null, false, { message: 'Incorrect password.' });
            }
            return done(null, user);
        });
    }
));
...