I am using nodejs with sequelize to setup my database, and currently i am trying to hash and salt my password, the hashing i already did, but now when i tyr to login i want to compare the password send in req.body with the hashed one, so i did this:
router.post('/', function (req, res, next) {
console.log("hi");
if (JSON.stringify(req.body) == "{}") {
return res.status(400).json({ Error: "Login request body is empty" });
}
if (!req.body.username || !req.body.password) {
return res.status(400).json({ Error: "Missing fields for login" });
}
var password = User.validPassword(req.body.password);
// search a user to login
User.findOne({ where: { username: req.body.username, password: password } }) // searching a user with the same username and password sended in req.body
.then(function (user) {
if (!user) {
return res.status(400).json({ Error: "There is no user with those fields" }); // if there is no user with specific fields send
}
return res.status(400).json({ Error: "loged in!" }); // username and password match
}).catch(function (err) {
return res.status(200).json({ message: "server issues when trying to login!" }); // server problems
});
});
my model is like this:
"use strict";
var sequelize = require('./index');
var bcrypt = require('bcrypt-nodejs');
module.exports = function (sequelize, DataTypes) {
var User = sequelize.define("User", {
username: DataTypes.STRING,
email: DataTypes.STRING,
password: DataTypes.STRING
}, {
classMethods: {
generateHash: function (password) {
console.log("hi");
return bcrypt.hashSync(password, bcrypt.genSaltSync(8), null);
},
validPassword: function (password) {
return bcrypt.compareSync(password, this.password);
}
}
});
return User;
}
i don't get any response from
var password = User.validPassword(req.body.password);
i tried to console.log but stilm no response, when i try to go to router /login it just doesn0t give me any response it is loading all the time, any sugestion?
To start with, your route defines a post action to / not /login. You can use postman to try that out if you don't have a front-end yet.
The validPassword method should be called on an instance of the User model so you should have written this portion of the code like this
router.post('/', function (req, res, next) {
console.log("hi");
if (JSON.stringify(req.body) == "{}") {
return res.status(400).json({ Error: "Login request body is empty" });
}
if (!req.body.username || !req.body.password) {
return res.status(400).json({ Error: "Missing fields for login" });
}
// search a user to login
User.findOne({ where: { username: req.body.username } }) // searching a user with the same username and password sended in req.body
.then(function (user) {
if(user && user.validPassword(req.body.password)) {
return res.status(200).json({message: "login successful"});
} else {
return res.status(401).json({message: "Unauthorized"});
}
}).catch(function (err) {
return res.status(200).json({ message: "server issues when trying to login!" }); // server problems
});
});
Also in you catch method, the status code should be something that depicts internal server error like 500. 200 is inappropriate.
Soo seems like I could answer my own question, with the help of Femi Oladeji, basicly the problem was related to the fact that i want to acess the methods trough a instance and not a model.
So when I tried to acces it with the user instance, there was no method that treat instances, so made some changes on my model.
"use strict";
var sequelize = require('./index');
var bcrypt = require('bcrypt-nodejs');
module.exports = function (sequelize, DataTypes) {
var User = sequelize.define("User", {
username: DataTypes.STRING,
email: DataTypes.STRING,
password: DataTypes.STRING
}, {
classMethods: {
generateHash: function (password) {
return bcrypt.hashSync(password, bcrypt.genSaltSync(8), null);
},
},
instanceMethods: {
validPassword: function (password) {
console.log(password, this.password)
return bcrypt.compareSync(password, this.password);
}
}
});
return User;
}
and there is the instanceMethods, that can treat the instance :)
Related
I have created an API for LogIn authentication. I have used Sequelize ORM for Mysql Database. Given below is an image of my users model which i have imported in my authentication code for login.
Models/users image
module.exports = (sequelize, Sequelize) => {
const Tutorial = sequelize.define("users", {
age: { type: Sequelize.INTEGER },
name: { type: Sequelize.STRING },
email: { type: Sequelize.STRING },
password: { type: Sequelize.STRING }
});
return Tutorial
};
Here below is the code for log in authentication. I have used findOne function for getting the email & password and i have used .then which is a promise function that returns the response.
var users = require('./models/users');
app.post('/login', (req, res) => {
var email = req.body.email;
var password = req.body.password;
users.findOne({ email: email, password: password })
.then(users => {
if (users == null) {
res.status(404).json({
message: 'Auth Failed'
})
}
else {
res.status(200).json({
message: 'Logg In succesfull'
})
}
})
})
But when i hit the API, it shows error
TypeError: users.findOne is not a function
at D:\Node\Task\server.js:39:11
Please help me fix it.
module.exports = (sequelize, Sequelize) => {
Because your function accepts 2 parameters, you'll have to pass the values as well in the require to get it working.
const sequel = require('sequelize');
const DataTypes = sequel.DataTypes;
const sequelizeInstance = new sequel(...) // import your sequelize config
var users = require('./models/users')(sequelizeInstance, DataTypes);
I know nothing about Sequelize, but inspired by #sid's answer, I guess something like this is cleaner than importing Sequelize in the main file, then pass it to the imported file... Just import your dependencies in each file.
models/users :
const sequel = require('sequelize');
const DataTypes = sequel.DataTypes;
const sequelizeInstance = new sequel(...) // import your sequelize config
module.exports = sequelize.define("users", {
age: { type: DataTypes.INTEGER },
name: { type: DataTypes.STRING },
email: { type: DataTypes.STRING },
password: { type: DataTypes.STRING }
})
Then simply import it :
const users = require('./models/users');
users.findOne({ email, password })
If this is still unanswered it could be because of two reasons. Either you're importing the model wrong, or its because you're calling the query without it being async (As I've had similar issues with this as a cause):
const { users } = require('./models')
async app.post('/login', (req, res) => {
try {
const email = req.body.email
const password = req.body.password
const user = await users.findOne({
where: {
email: email,
password: password // Remove this if you have a verify password func
}
})
if (!user) {
res.status(403).send({
error: "Password or email is wrong"
})
}
// You should really encrypt the passwords and have a compare/verify function instead
// Example
if (!bcrypy.compareSync(password, user.password)) {
res.status(403).send({
error: "Password or email is wrong"
})
}
res.staus(200).send(user) // Save user to store/cache on client
} catch (err) {
console.log(err)
res.status(500).send({
error: "Error occured during login"
})
}
})
Once you have more of your frontend setup, you should send the user back to the client and then store it in client side cache or smth, so you don't need to query the server for logged in user data everytime.
I have created a model and the name of the table is users. In the Model, i have a method generateToken which is used to generate the web token.
I have used sequelized ORM.
module.exports = (sequelize, Sequelize) => {
const Tutorial = sequelize.define("users", {
age: {
type: Sequelize.INTEGER
},
name: {
type: Sequelize.STRING
},
email: {
type: Sequelize.STRING
},
password: {
type: Sequelize.STRING
}
});
Tutorial.generateToken = async function () {
try {
const token = jwt.sign({ _id: this.id }, "ThisIsTaskApp")
console.log(token)
}
catch (error) {
response.send('there is an error' + error)
console.log('there is an error' + error)
}
}
return Tutorial;
};
I want to create a web token when my email id and password matches, so for that i have used the generateToken but i am getting an error
TypeError: user.generateToken is not a function
I believe i have error with javascript importing the generateToken function.
const jwt = require('jsonwebtoken')
const user = db.users;
const generateToken = require('./models/users')
app.post('/login', async (req, res) => {
try {
var email = req.body.email;
var password = req.body.password;
await user.findOne({ where: { email: email, password: password } })
.then(user => {
if (user === null) {
res.status(404).json({
message: 'Auth Failed'
})
}
else {
const token = user.generateToken()
res.status(200).json(user)
}
})
}
catch (error) {
console.log(error)
return res.json({ 'status': 400 })
}
})
Please help me fix this issue and generating web token.
Try using
generateToken.generateToken()
there instead of
user.generateToken()
Because you are basically exporting the model of users in generate token variable, so that function is accessible from that variable not from user variable.
There is some issue with your code related to async, please try this one
const user = db.users;
app.post("/login", async (req, res) => {
try {
var email = req.body.email;
var password = req.body.password;
const userdata = await user.findOne({ where: { email: email, password: password } });
if (userdata === null) {
return res.status(404).json({
message: "Auth Failed",
});
}
const token = await userdata.generateToken();
console.log("🚀 ~ token", token)
return res.status(200).json(userdata);
} catch (error) {
console.log(error);
return res.json({ status: 400 });
}
});
I think you need to require jsonwebtoken in /models/users as well as in the route handler file
I made a login with bcrypt.
I also made a page where users can edit their information, like their bio etc.
Each time an user edit his bio on this page the hash from bcrypt change, which is normal i suppose, but the user login back, the password is wrong...
I used the same model for mongoDb for the user when he log in and when he edit his data.
I started node.js recently so I apologize if my question is stupid,,,
The controller code with the Post :
app.post('/settings-user', mid.requiresLogin, function(req, res, next){
User.findById(req.session.userId, function (err, user) {
// todo: don't forget to handle err
if (!user) {
return res.redirect('/edit');
}
// good idea to trim
var bio = req.body.bio.trim();
// validate
if (!bio) { // simplified: '' is a falsey
req.flash('error', 'One or more fields are empty');
return res.redirect('/settings-user'); // modified
}
// no need for else since you are returning early ^
user.bio = bio;
// don't forget to save!
user.save(function (err) {
// todo: don't forget to handle err
res.redirect('/settings-user/');
});
});
});
The User model :
app.post('/settings-user', mid.requiresLogin, function(req, res, next){
User.findById(req.session.userId, function (err, user) {
// todo: don't forget to handle err
if (!user) {
return res.redirect('/edit');
}
// good idea to trim
var bio = req.body.bio.trim();
// validate
if (!bio) { // simplified: '' is a falsey
req.flash('error', 'One or more fields are empty');
return res.redirect('/settings-user'); // modified
}
// no need for else since you are returning early ^
user.bio = bio;
// don't forget to save!
user.save(function (err) {
// todo: don't forget to handle err
res.redirect('/settings-user/');
});
});
});
The User model :
var mongoose = require('mongoose');
var bcrypt = require('bcrypt');
var UserSchema = new mongoose.Schema({
email: {
type: String,
unique: true,
required: true,
trim: true
},
name: {
type: String,
required: true,
trim: true
},
password: {
type: String,
required: true
},
bio: {
type: String
}
});
// authenticate input against database documents
UserSchema.statics.authenticate = function(email, password, callback) {
User.findOne({ email: email })
.exec(function (error, user) {
if (error) {
return callback(error);
} else if ( !user ) {
var err = new Error('User not found.');
err.status = 401;
return callback(err);
}
bcrypt.compare(password, user.password , function(error, result) {
if (result === true) {
return callback(null, user);
} else {
return callback();
}
})
});
}
// hash password before saving to database
UserSchema.pre('save', function(next) {
var user = this;
bcrypt.hash(user.password, 10, function(err, hash) {
if (err) {
return next(err);
}
user.password = hash;
next();
})
});
var User = mongoose.model('User', UserSchema);
module.exports = User;
the pug file :
div
form(method='post', action='/settings-user')
label ADD BIO
br
input(type='text', name='bio', placeholder='Enter something', required='')
input(type='submit', value='Add Bio')
</body>
If anyone could help,,,
thank you!
i follow this question but i still didn't figure out how to solve it, it says to use instanceMethods option to use the methods in the model thats exactly what i did:
"use strict";
var sequelize = require('./index');
var bcrypt = require('bcrypt-nodejs');
module.exports = function (sequelize, DataTypes) {
var User = sequelize.define("User", {
username: DataTypes.STRING,
email: DataTypes.STRING,
password: DataTypes.STRING
}, {
instanceMethods: {
generateHash: function (password) {
console.log("hi");
return bcrypt.hashSync(password, bcrypt.genSaltSync(8), null);
},
validPassword: function (password) {
return bcrypt.compareSync(password, this.password);
}
},
});
return User;
}
after i use that i tried to test it generating a password hash on my register route like this:
var express = require('express');
var User = require('../../models').User;
var router = express.Router();
/* GET users listing. */
router.post('/', function (req, res, next) {
if (JSON.stringify(req.body) == "{}") {
return res.status(400).json({ Error: "Register request body is empty" });
}
if (!req.body.email || !req.body.username || !req.body.password) {
return res.status(400).json({ Error: "Missing fields for registration" });
}
var password = User.instaceMethods.generateHash(req.body.password);
User.create({
username: req.body.username,
email: req.body.email,
password: password
}).then(function () {
return res.status(200).json({message: "user created"});
})
});
module.exports = router;
with this when i go to my /register, i don't get any response even a console.log in the password doesn't show me anything i tried to see if it enters the generetehash but, it doesn't enter the method, what is going wrong here?
Add your generateHash as a classMethod of your model, not an instance method.
var User = sequelize.define('user', {
....
}, {
classMethods: {
generateHash: function() {
...
}
})
Because you don't have an instance yet, not before User.create().
Then you can call it using User.generateHash()
And also you should catch (and probably at least log) sequelize exceptions. Add
.catch(function(err){
console.log(err)
})
after your then()
I am using Mongo in my app, I have a db named test, what I am doing now is just an app to understand the workflow among Angular, Nodejs and Mongo.
The issue I have right now, or actually I don't know if this an issue but, I created an user with the name of User1 and a given email address, then I went to the app and saved some stuff, I logged out and then logged in again to confirm that the info I saved was there, and YES! the info was there it doesn't matter how many times and logged in and logged out.
Then I created another user with the name of User2 and obviously a different email address, but when I logged in, the information I had save from User1 was there.
I am on a local environment:
Node version: 5.0.0
MongoDB shell version: 3.2.0
this is what I have regarding the user schema
var userSchema = new mongoose.Schema({
name: { type: String, trim: true, required: true },
email: { type: String, unique: true, lowercase: true, trim: true },
password: String,
facebook: {
id: String,
email: String
},
google: {
id: String,
email: String
}
});
userSchema.pre('save', function(next) {
var user = this;
if (!user.isModified('password')) return next();
bcrypt.genSalt(10, function(err, salt) {
if (err) return next(err);
bcrypt.hash(user.password, salt, 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 User = mongoose.model('User', userSchema);
mongoose.connect('localhost');
and here in Nodejs the signup route
app.post('/auth/signup', function(req, res, next) {
var user = new User({
name: req.body.name,
email: req.body.email,
password: req.body.password
});
user.save(function(err) {
if (err) return next(err);
res.sendStatus(200);
});
});
and the service un Angular
signup: function(user) {
return $http.post('/auth/signup', user)
.success(function() {
$location.path('/login');
});
}
the signup controller
.controller('SignupCtrl', function($scope, Auth) {
$scope.signup = function() {
Auth.signup({
name: $scope.displayName,
email: $scope.email,
password: $scope.password
});
};
Here is the logging part:
Node:
app.post('/auth/login', function(req, res, next) {
User.findOne({ email: req.body.email }, function(err, user) {
if (!user) return res.status(401).send('User does not exist');
user.comparePassword(req.body.password, function(err, isMatch) {
if (!isMatch) return res.status(401).send('Invalid email and/or password');
var token = createJwtToken(user);
res.send({ token: token });
});
});
});
Angular Service
login: function(user) {
return $http.post('/auth/login', user).success(function(data) {
$window.localStorage.token = data.token;
var payload = JSON.parse($window.atob(data.token.split('.')[1]));
$rootScope.currentUser = payload.user;
$location.path('/');
})
}
Controller
.controller('LoginCtrl', function($scope, Auth) {
$scope.login = function() {
Auth.login({ email: $scope.email, password: $scope.password });
};
Update
var tokenSecret = 'whatever goes here';
function ensureAuthenticated(req, res, next) {
if (req.headers.authorization) {
var token = req.headers.authorization.split(' ')[1];
try {
var decoded = jwt.decode(token, tokenSecret);
if (decoded.exp <= Date.now()) {
res.status(400).send('Access token has expired');
} else {
req.user = decoded.user;
return next();
}
} catch (err) {
return res.status(500).send('Error parsing token');
}
} else {
return res.sendStatus(401);
}
}
function createJwtToken(user) {
var payload = {
user: user,
iat: new Date().getTime(),
exp: moment().add(7, 'days').valueOf()
};
return jwt.encode(payload, tokenSecret);
}
I created this Gist with the full code
Here is the Github repository with all of the info
what do you think its happening?