How to use bcrypt.compare in sails js schema? - node.js

I have a user model like this:
module.exports = {
attributes: {
email: {
type: 'string',
isEmail: true,
unique: true,
required: true
},
password: {
type: 'string',
required: true
}
},
beforeCreate: (value, next) => {
bcrypt.hash(value.password, 10, (err, hash) => {
if (err){
throw new Error(err);
}
value.password = hash;
next();
});
},
};
Now when I want to match the password during login, how do I decrypt the password, if possible I would prefer to perform it in the user model file.
controller/ login.js
module.exports = {
login: async (req, res) => {
try{
const user = await User.findOne({email: req.body.email});
if (!user){
throw new Error('Failed to find User');
}
// here I want to match the password by calling some compare
//function from userModel.js
res.status(201).json({user: user});
}catch(e){
res.status(401).json({message: e.message});
}
},
};

first try to find the user with the given username by user
const find = Users.find(user=>user.username===req.body.username)
if(!find){
res.send('User Not Found')
}
else{
if( await bcrypt.compare(req.body.password,find.password)){
//now your user has been found
}
else{
//Password is Wrong
}
}
You must use bcrypt.compare(a,b)
a = given password by user
b = original password if username exist
hope it solve your problem

Related

How to make a specific user log out? NodeJS-Express-MongoDB

I have admin role and when I block some user, I want to log the user out immediately.
req.session.destroy() is not the case as it log out me. Thanks in advance.
app.js
mongoose.connect('mongodb://127.0.0.1/nodeblog_db', {
useNewUrlParser: true,
useUnifiedTopology: true,
});
app.use(expressSession({
secret: 'testotesto',
resave: false,
saveUninitialized: true,
store: connectMongo.create({mongoUrl : 'mongodb://127.0.0.1/nodeblog_db'})
}))
// parse application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: false }))
// parse application/json
app.use(bodyParser.json())
login route
router.get('/login', (req, res) => {
res.render('site/login');
});
router.post('/login', (req, res) => {
const { email, password } = req.body;
User.findOne({ email }, (error, user) => {
if (user) {
user.comparePassword(password, (matchError, isMatch) => {
if (matchError) {
throw matchError;
}
else if (isMatch) {
req.session.userId = user._id; //**************
res.redirect('/');
}
else if (!isMatch) {
res.redirect('/users/login');
}
})
}
else {
res.redirect('/users/register');
}
});
});
My User Model
I have a banned field in my database. When I want to block a user, I set that field as true.
const mongoose = require('mongoose');
const bcrypt = require("bcryptjs");
const UserSchema = new mongoose.Schema({
username: { type: String, required: true, unique: true },
email: { type: String, required: true, unique: true },
password: { type: String, required: true },
verified: { type: Boolean, default: false },
auth: { type: String, default: false },
banned: { type: Boolean, default: false }
});
UserSchema.pre("save", function (next) {
const user = this
if (this.isModified("password") || this.isNew) {
bcrypt.genSalt(10, function (saltError, salt) {
if (saltError) {
return next(saltError)
} else {
bcrypt.hash(user.password, salt, function (hashError, hash) {
if (hashError) {
return next(hashError)
}
user.password = hash
next()
})
}
})
} else {
return next()
}
})
UserSchema.methods.comparePassword = function (password, callback) {
bcrypt.compare(password, this.password, function (error, isMatch) {
if (error) {
return callback(error)
} else {
callback(null, isMatch)
}
})
}
module.exports = mongoose.model('User', UserSchema);
I use this code to check if user is logged in:
if(req.session.userId){
//the user is logged in
}
So The Default way I would try solving this is to add a middleware after the auth check
This is because am sure its gonna contain req.session.userId = user._id;
// Import Your Db Model
const checkBan = (req,res,next)=>{
// If you don't pass your user state into req.user
User.findOne({ _id:req.session.userId }, (error, user) => {
if(error){
next(err)
}else{
// The User Would have been authenticated
// Therefore User exist
if(user.banned){
// User Is Banned so handle it as you like
res.send("Your Account is banned - other messages")
}else{
// Users Aren't Banned so continue
next()
}
}
})
}
module.exports = checkBan;
You Can Now Import this After your Authentication checker middleware on routes you want the banned user to be unable to access
Now when you change the state to ban its renders this message and hinders any further interaction with your system from the user

sequelize promise always return false

I'm creating a react-native app.
The flow works like this, a customer has to input an email and password to signup and the data will be saved in the database. Before the data is saved, I've used the pre-hook beforeValidate to hash the password using bcrypt.
Until here, everything worked fine, but I can't seem to return true when the promise from instanceMethod comparePassword is made.
I have a customer model Customer.js file like below:
const Sequelize = require('sequelize');
const bcrypt = require('bcrypt');
const db = require('../config/database');
const Customer = db.define('customer', {
id : {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true,
allowNull: false
},
email : {
type: Sequelize.STRING,
unique: true,
allowNull: false
},
password : {
type: Sequelize.STRING,
allowNull: false
},
createdAt : {
type: Sequelize.NOW
},
updatedAt : {
type: Sequelize.NOW
}
}, {
hooks: {
afterValidate: (customer) => {
customer.password = bcrypt.hashSync(customer.password, 10);
}
},
instanceMethods: {
comparePassword: (candidatePassword) => {
return new Promise((resolve, reject) => {
bcrypt.compareSync(candidatePassword, this.password, (err, isMatch) => {
if(err) {
return reject(err);
}
if(!isMatch) {
return reject(false);
}
resolve(true);
});
});
}
}
});
module.exports = Customer;
and a snippet of authRoutes.js file like below:
router.post('/login', async (req, res) => {
const { email, password } = req.body;
if ( !email || !password ) {
return res.status(422).send({error: 'Must provide email and password!'});
}
const customer = await Customer.findOne({ where: {email} });
if(!customer) {
return res.status(422).send({error: '1. Invalid email or password!'});
}
try {
await customer.comparePassword(password);
const token = jwt.sign({ email }, 'MY_SECRET_KEY');
res.send({ email, token });
} catch(err) {
return res.status(422).send({error: '2. Invalid email or password!'});
}
});
There's no error or anything but it always catches the "2. invalid email or password" error even tho I've input the correct credentials. Any kind of help is appreciated. Thank you.
I have created a function (comparePassword) to compare password with hashed password Which use bcrypt to compare password.
const bcrypt = require('bcryptjs');
const customer = await Customer.findOne({ where: { email } });
const comparePassword = (hashedPassword, password) => {
return bcrypt.compareSync(password, hashedPassword);
};
try {
if (!comparePassword(customer.password, password) {
return res.status(422).send({ error: '2. Invalid email or password!' });
}
else {
const token = jwt.sign({ email }, 'MY_SECRET_KEY');
return res.status(200).send({ email, token });
}
} catch (err) {
console.log(err)
return res.status(500).send({ error: 'something bad happened on server' });
}
Customer can be defined as a class in Sequelize 4+. Then instance methods can be added as regular class instance methods.
class Customer extends Sequelize.Model {
static table_schema = {
id: {
type: Sequelize.INTEGER,
primaryKey: true,
autoIncrement: true,
allowNull: false
},
...
}
static table_options = {
...
}
static init(sequelize){
return super.init(this.table_schema, { this.table_options, ...sequelize })
}
static associate(models) {
}
async comparePassword(candidatePassword){
return bcrypt.compare(candidatePassword, this.password)
}
}
Customer.addHook('afterValidate', async function(customer){
customer.password = await bcrypt.hash(customer.password, 10);
})
Then you should be able to make use of the async comparePassword function in your route, similar to Arya's answer
router.post('/login', async (req, res) => {
try {
const { email, password } = req.body;
if ( !email || !password ) {
return res.status(422).send({error: 'Must provide email and password!'});
}
const customer = await Customer.findOne({ where: {email} });
if (!customer) {
console.log('Failed login [%s] not found', email)
return res.status(422).send({error: 'Invalid email or password!'});
}
const auth = await customer.comparePassword(password);
if (!auth) {
console.log('Failed login [%s] bad password', email)
return res.status(422).send({error: 'Invalid email or password!'});
}
const token = jwt.sign({ email }, 'MY_SECRET_KEY');
res.send({ email, token });
}
catch(err) {
console.error('Failed to process request', err)
return res.status(500).send({error: 'Internal Server Error'});
}
});

How to make password validation in NodeJS with Mongoose

I have registration form with username, mail, password and password2. I want to verify passwords that they actually match. I verify practically everything in Mongoose Scheme but I cannot find any useful information in documentation how to grab password2 without actually saving it to database. (I have function to crypt password which runs only before saving)
const userSchema = new mongoose.Schema({
username: {
type: String,
unique: true,
required: true,
trim: true,
validate(value) {
if (!validator.isAlphanumeric(value , 'pl-PL')) {
throw new Error('Name cannot contain special characters.')
}
}
},
email: {
type: String,
unique: true,
required: true,
trim: true,
lowercase: true,
validate(value) {
if (!validator.isEmail(value)) {
throw new Error('Email is invalid')
}
}
},
password: {
type: String,
required: true,
validate(value) {
console.log(value)
if(value !== this.password2) {
throw new Error("Passwords don't match. Try again.")
}
if(value.length < 8) {
throw new Error("Passwords is too short. At least 8 characters.")
}
}
},
tokens: [{
token: {
type: String,
required: true
}
}]
})
You don't need to make password2 a part of userSchema. The better way is to make a compare password function like this:
UserSchema.methods.comparePassword = function(plaintext, callback) {
return callback(null, Bcrypt.compareSync(plaintext, this.password));
};
also you can make a use of Schema.pre:
UserSchema.pre("save", function(next) {
if(!this.isModified("password")) {
return next();
}
this.password = Bcrypt.hashSync(this.password, 10);
next();
});
After this, you need to call the compare function from user controller. Something like this (depending on your logic):
var user = await UserModel.findOne({ username: request.body.username }).exec();
if(!user) {
return response.status(400).send({ message: "The username does not exist" });
}
user.comparePassword(request.body.password, (error, match) => {
if(!match) {
return response.status(400).send({ message: "The password is invalid" });
}
});
For details you can read this excellent article.
You can check password and password2 in your register route, and if they are same you can continue to register.
A sample register route would be like this:
router.post("/register", async (req, res) => {
try {
const { username, email, password, password2 } = req.body;
if (password !== password2) return res.status(400).send("Passwords dont match");
let user = await User.findOne({ email });
//or
//let user = await User.findOne({ username });
if (user) return res.status(400).send("User already registered.");
user = new User({ username, email, password });
user = await user.save();
//todo: at this point you may generate a token, and send to the client in response header or body
res.send(user);
} catch (err) {
console.log(err);
res.status(500).send("Server error");
}
});

access the user._id in nodejs

I'm pretty new with node.js and I'm trying to implement simple user registration and login form using Node.js, Express, bcrypt, express-session and mongoose.
Whenever the user log in, I want to set the value of req.session.userID to user's id. When I trace the code I can't find the problem. I followed up the tutorial in this link and everything seems to be similar.
Schema:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var bcrypt = require('bcrypt');
var userSchema = new Schema({
teamName: {
type: String,
unique: true,
trim: true,
required: true
},
faculty: {
type: String,
required: true
},
email: {
required: true,
unique: true,
trim: true,
type: String
},
password: {
required: true,
type: String
},
score: {
type: Number,
default: 0
}
});
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();
});
});
userSchema.statics.authenticate = (email, password, callback) => {
userModel.findOne({email: email}, (err, user) => {
if (err) return callback(err);
else if (!user) {
console.log('User not found!')
}
else {
bcrypt.compare(password, user.password, (err, result) => {
if (result) {
callback(null, true)
}
else {
return callback()
}
})
}
})
}
var userModel = mongoose.model('User', userSchema);
module.exports = userModel;
server:
var userModel = require('./../models/users');
router.post('/login', (req, res) => {
var email = req.body.email;
var password = req.body.password;
userModel.authenticate(email, password, (err, user) => {
console.log(user)
if (err) {
console.log(err)
}
else if (!user) {
console.log('Wrong Password')
}
else {
req.session.userId = user._id;
console.log(req.session.userId);
}
})
});
Where I have logged the value of req.session.userId it returns undefined! Where is the problem?
The problem is that the callback is returning TRUE. the callback should be returning the user data. callback(null, user)
bcrypt.compare(password, user.password, (err, result) => {
if (result) {
callback(null, true)
}
Should be
bcrypt.compare(password, user.password, (err, result) => {
if (result) {
callback(null, user)
}

Node JS Authentications with passport-jwt unauthorized

Im trying to setup my Node JS API.
I have a User model :
// Dependencies
var restful = require('node-restful');
var mongoose = restful.mongoose;
var bcrypt = require('bcrypt');
// Schema
var userSchema = new mongoose.Schema({
username: {
type: String,
required: true,
unique: true},
firstname: {
type: String,
required: true
},
lastname: {
type: String,
required: true
},
email: {
type: String,
required: true,
unique: true,
lowercase: true
},
password: {
type: String,
required: true},
},
{
timestamps: true
});
// Saves the user's password hashed
userSchema.pre('save', function (next) {
var user = this;
if (this.isModified('password') || this.isNew) {
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();
});
});
} else {
return next();
}
});
// Use bcrypt to compare passwords
userSchema.methods.comparePassword = function(pw, cb) {
bcrypt.compare(pw, this.password, function(err, isMatch) {
if (err) {
return cb(err);
}
cb(null, isMatch);
});
};
module.exports = restful.model('Users', userSchema);
I want to use passport with jwt for authentication :
// Dependencies
var JwtStrategy = require('passport-jwt').Strategy;
var ExtractJwt = require('passport-jwt').ExtractJwt;
var config = require('../config/database');
// Load models
var User = require('../models/user');
// Logique d'authentification JWT
module.exports = function(passport) {
var opts = {};
opts.jwtFromRequest = ExtractJwt.fromAuthHeaderWithScheme('JWT');
opts.secretOrKey = config.secret;
opts.audience = 'localhost';
passport.use(new JwtStrategy(opts, function(jwt_payload, done) {
User.findById(jwt_payload._id, function(err, user) {
if (err) {
return done(err, false);
}
if (user) {
done(null, user);
} else {
done(null, false);
}
});
}));
passport.use(new JwtStrategy(opts, function(jwt_payload, done) {
Company.findById(jwt_payload._id, function(err, company) {
if (err) {
return done(err, false);
}
if (company) {
done(null, company);
} else {
done(null, false)
}
});
}));
};
And my route for authentication :
// User
router.post('/users/login', (req, res) => {
User.findOne({
email: req.body.email
}, (err, user) => {
if (err) throw err;
if (!user) {
res.json({success: false, message: 'Authentication failed. User not found.'});
} else {
// Check if passwords matches
user.comparePassword(req.body.password, (err, isMatch) => {
if (isMatch && !err) {
// Create token if the password matched and no error was thrown
var token = jwt.sign(user, config.secret, {
expiresIn: 10080 // in seconds
});
res.json({success: true, token: 'JWT ' + token, user: {
id: user._id,
username: user.username,
email: user.email
}});
} else {
res.json({success: false, message: 'Authentication failed. Passwords did not match.'});
}
});
}
});
});
Everything work great on postman.
The token is correctly generated and signed with user's informations.
But i have a problem with the authentication on a protected route :
router.get('/users/profile', passport.authenticate('jwt', { session: false }), function(req, res) {
res.send('It worked! User id is: ' + req.user._id + '.');
});
Everytime, it gives me an "Unauthorized 401" Error.
I really dont know where is the problem, i think the problem is around jwtFromRequest, i also tried with Bearer but it also doesn't work...
I think a good option to avoid this kind of problems is to start from a base project that uses this authentication strategy, and after you have that working, modify it with your functionality.
Here you have an example with jwt authentication strategy and Refresh token implementation: https://solidgeargroup.com/refresh-token-autenticacion-jwt-implementacion-nodejs?lang=es

Resources