Использование BCrypt с Sequelize Model

Я пытаюсь использовать пакет bcrypt-nodejs с моей моделью sequelize и пытаюсь следовать учебному курсу, чтобы включить хеширование в мою модель, но я получаю сообщение об ошибке в generateHash. Кажется, я не могу понять эту проблему. Есть ли лучший способ включить bcrypt?

Ошибка:

/Users/user/Desktop/Projects/node/app/app/models/user.js:26
User.methods.generateHash = function(password) {
                          ^
TypeError: Cannot set property 'generateHash' of undefined
    at module.exports (/Users/user/Desktop/Projects/node/app/app/models/user.js:26:27)
    at Sequelize.import (/Users/user/Desktop/Projects/node/app/node_modules/sequelize/lib/sequelize.js:641:30)

Модель:

var bcrypt = require("bcrypt-nodejs");

module.exports = function(sequelize, DataTypes) {

var User = sequelize.define('users', {
    annotation_id: {
        type: DataTypes.INTEGER,
        autoIncrement: true,
        primaryKey: true
    },
    firstName: {
        type: DataTypes.DATE,
        field: 'first_name'
    },
    lastName: {
        type: DataTypes.DATE,
        field: 'last_name'
    },
    email: DataTypes.STRING,
    password: DataTypes.STRING,

}, {
    freezeTableName: true
});

User.methods.generateHash = function(password) {
    return bcrypt.hashSync(password, bcrypt.genSaltSync(8), null);
};

User.methods.validPassword = function(password) {
    return bcrypt.compareSync(password, this.local.password);
};
    return User;
}

Ответ 1

Методы должны быть представлены в аргументе "options" метода sequelize.define

const bcrypt = require("bcrypt");

module.exports = function(sequelize, DataTypes) {
    const User = sequelize.define('users', {
        annotation_id: {
            type: DataTypes.INTEGER,
            autoIncrement: true,
            primaryKey: true
        },
        firstName: {
            type: DataTypes.DATE,
            field: 'first_name'
        },
        lastName: {
            type: DataTypes.DATE,
            field: 'last_name'
        },
        email: DataTypes.STRING,
        password: DataTypes.STRING
    }, {
        freezeTableName: true,
        instanceMethods: {
            generateHash(password) {
                return bcrypt.hash(password, bcrypt.genSaltSync(8));
            },
            validPassword(password) {
                return bcrypt.compare(password, this.password);
            }
        }
    });

    return User;
}

Ответ 2

Другая альтернатива: Использовать режим асинхронного вызова hook и bcrypt

User.beforeCreate((user, options) => {

    return bcrypt.hash(user.password, 10)
        .then(hash => {
            user.password = hash;
        })
        .catch(err => { 
            throw new Error(); 
        });
});

Ответ 3

Там есть учебник о том, как получить систему ceelize/postgreSQL auth с помощью hooks и bcrypt.

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

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

Асинхронные методы на самом деле настолько элегантны и красивы, я чувствую себя идиотом, но в любом случае:

В разделе создания/экземпляра пользователя он использовал следующий код:

hooks: {
  beforeCreate: (user) => {
    const salt = bcrypt.genSaltSync();
    user.password = bcrypt.hashSync(user.password, salt);
  }
},
instanceMethods: {
  validPassword: function(password) {
    return bcrypt.compareSync(password, this.password);
  }
}    

Более новые версии Sequelize не похожи на методы экземпляра, объявляемые таким образом, и несколько человек объяснили, как исправить это (в том числе и тот, кто опубликовал исходный учебник):

В первоначальном комментарии все еще использовались синхронные методы, которые были немного разочаровывающими.

User.prototype.validPassword = function (password) {
    return bcrypt.compareSync(password, this.password);
};

Очевидно, я идиот, потому что все, что вам нужно сделать, чтобы эти функции были асинхронны, такова:

Async beforeCreate bcrypt genSalt и genHash:

beforeCreate: async function(user) {
    const salt = await bcrypt.genSalt(10); //whatever number you want
    user.password = await bcrypt.hash(user.password, salt);
}

User.prototype.validPassword = async function(password) {
    return await bcrypt.compare(password, this.password);
}

Для кода beforeCreate это буквально ничего не меняет - похоже, что это работает.

В приложении node.js на логинном пути, где вы проверяете пароль, есть раздел findOne:

User.findOne({ where: { username: username } }).then(function (user) {
    if (!user) {
        res.redirect('/login');
    } else if (!user.validPassword(password)) {
        res.redirect('/login');
    } else {
        req.session.user = user.dataValues;
        res.redirect('/dashboard');
    }
});

Все, что вам нужно сделать, это добавить слова async и await:

User.findOne({ where: { username: username } }).then(async function (user) {
    if (!user) {
        res.redirect('/login');
    } else if (!await user.validPassword(password)) {
        res.redirect('/login');
    } else {
        req.session.user = user.dataValues;
        res.redirect('/dashboard');
    }
});

Если я сделал что-то не так, пожалуйста, дайте мне знать - спасибо большое, надеюсь, это поможет кому-то там.