hashing password using brcrpt in "pre" - node.js

i am trying to store a hash of the password entered in a form using bcrypt in mongodb
this is the form
<form action="/register" method="POST">
<label for="firstname">Firstname</label>
<input type="text" name="firstname" id="fname" ></p><br/>
<label for="lastname">Lastname</label>
<input type="text" name="lastname" ></p><br/>
<label for="email">Email</label>
<input type="email" name="email" ></p><br/>
<label for="">Password</label>
<input type="password" name="password" ></p><br/>
<input type="submit" value="submit">
this is my mongoose schema
const userschema = new mongoose.Schema({ // define a schema
fname:{type:String,trim:true},
lname:{type:String,trim:true},
email:{type:String,lowercase:true,trim:true},
pass:{type:String}
});
and this is my pre function that is to be performed before save is called in my handler
userschema.pre('save',async function(next){
const user=this;
console.log(user); //in this log the user object this is containing plaintext value of password
await bcrypt.hash(user.pass, 8, function(err, hash) {
if(err){throw new Error(err);}
else{
user.pass=hash;
console.log(user); //and in this log the user object is containing hashed value as expected
}
})
next()}
);
and this is my handler for saving the user in the database
app.post("/register",function(req,res){
const errors = validationResult(req);
if (!errors.isEmpty()) {
console.log(errors.array());
return res.status(400).render("register",{ errors: errors.array() });
}
console.log(req.body); //out of context question but for some reason only firstname and email are visible in req.body object can anybody body explain
const usave=new user({
fname:req.body.firstname,
lname:req.body.lastname,
email:req.body.email,
pass:req.body.password
})
usave.save((err,doc)=>{
if(err){res.send(err);}
else{
res.send("success");}
});
});
when the route is handled the user is being saved succesfully but the password stored in the database is not being hashed.
I did make user.pass=hash in the schema.pre('save') function but its not being stored in the database as hash just being stored as plain text
can anybody tell me what is wrong with this code?how do i save the hashpassword to database?

userschema.pre('save', async function(next) {
var user = this;
await bcrypt.hash(user.pass, 8, function(err, hash) {
if (err) {
return next(err);
}
user.pass = hash;
next();
})
});
The above code will accomplish your goal of always hashing the password when a document is saved to the DB, passwords are not hashed until the document is saved.

Related

Form validation with flash messages in express js

I am trying to validate a form in express js before i post it onto the mongodb which i am using as a backend. My user.js for registration page looks something like this -
router.post('/register', (req, res) => {
userreg.register(
// eslint-disable-next-line new-cap
new userreg({
firstname: req.body.firstname,
lastname: req.body.lastname,
username: req.body.email,
usn: req.body.usn, // validate so that no space is taken or else request modal wont work
course: req.body.course,
}),
req.body.password,
(err) => {
if (err) {
console.log(err);
res.render('register', { user: 'error' });
} else {
console.log('no error');
res.render('submit-success', { username: req.body.firstname });
}
}
);
});
and my register.ejs looks something like this -
<form class="user" action = '/users/register' method="POST">
<div class="form-group row">
<div class="col-sm-6 mb-3 mb-sm-0">
<input type="text" class="form-control form-control-user" id="exampleFirstName" name="firstname" placeholder="First Name">
</div>
<div class="col-sm-6">
<input type="text" class="form-control form-control-user" id="exampleLastName" name="lastname" placeholder="Last Name">
</div>
</div>
<div class="form-group">
<input type="email" class="form-control form-control-user" id="exampleInputEmail" name="email" placeholder="Email Address">
</div>
<div class="form-group">
<input type="text" class="form-control form-control-user" id="exampleInputUSN" name="usn" placeholder="USN">
</div>
<div class="form-group">
<select class=" form-control selectpicker" name="course">
<optgroup label="Course"></optgroup>
<option selected hidden disabled>Course</option>
<option value="mca">Computer Applications</option>
<option value="mba">Business Administration</option>
</optgroup>
</select>
</div>
<div class="form-group">
<input type="password" class="form-control form-control-user" id="exampleInputPassword" name="password" placeholder="Password">
</div>
<div class="text-center">
<input type="submit" class = "btn btn-primary btn-user" value="Register">
</div>
</form>
By going through many sources on the internet(since i'm very very new to express js and im doing it as a part of my college project and since i can't consult any teachers for assistance during lockdown times) , I got to know that the validation part has to be implemented in user.js. Please help me with the code for validation and also displaying flash messages if field empty for atleast one field so that i can have a start atleast.
Thank you in advance
EDIT :
I Used the express-validator and ended up with the following changes -
var flash = require('connect-flash');
var app = express();
app.configure(function () {
app.use(express.cookieParser('keyboard cat'));
app.use(express.session({ cookie: { maxAge: 60000 } }));
app.use(flash());
});
app.get('/flash', function (req, res) {
// Set a flash message by passing the key, followed by the value, to
req.flash().
req.flash('info', 'There is an Error!')
res.redirect('/');
});
app.get('/', function (req, res) {
// Get an array of flash messages by passing the key to req.flash()
res.render('index', { messages: req.flash('info') });
});
const { check, validationResult } = require('express-validator');
router.post('/register', [
check('firstname', 'Please enter your first
name').exists().trim().escape().not().isEmpty(),
check('lastname', 'Please enter your last
name').exists().trim().not().isEmpty(),
check('username', 'Please enter an
email').exists().trim().not().isEmpty(),
check('usn', 'Please enter USN').exists().trim().not().isEmpty(),
check('course', 'Please enter Course').exists().trim().not().isEmpty(),
check('password', 'Please enter
password').exists().trim().not().isEmpty(),
], (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
req.flash('message', `${errors}`);
res.redirect('/users/register');
} else {
userreg.register(
// eslint-disable-next-line new-cap
new userreg({
firstname: req.body.firstname,
lastname: req.body.lastname,
username: req.body.email,
usn: req.body.usn, // validate so that no space is taken or else
request modal wont work
course: req.body.course,
})),
res.render('submit-success', { username: req.body.firstname });
}
}
);
And as a result , the if (!errors.isEmpty()) is being invoked but there is no flash message being displayed. Am i missing something else ?
I am assuming you are using connect-flash.
const { check, validationResult } = require('express-validator');
router.post('/register', [
check('firstname', 'Please enter your first name').exists().trim().escape().not().isEmpty(),
check('lastname', 'Please enter your last name').exists().trim().not().isEmpty(),
check('email', 'Please enter an email').exists().trim().not().isEmpty(),
// ...
], (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
req.flash('message', `${errors}`);
res.redirect('register');
}
else {
// your code here!
}
});
You might want to consider creating middleware to handle validation errors, to make your code cleaner.

how to stop a user from logging in twice from same account but different tab

I am working on a project , first i did user signup and login based on jwt authentication * note i am using reactjs * i want to disallow users from logging in several time from different tabs or browsers , what shall i do ? how to implement such service and where? i will provide the whol code that may be helpful
i want some one to help me implement not just give me the logic
This is my main express file :
//the main framework , express
var express = require('express');
//path , used for joining pathes
var path = require('path');
//cross origin resource sharing
var cors= require('cors')
//body parser used to take data from forms , to be used later on
var bodyParser=require("body-parser");
//for security reasons , http security(not https)
var helmet= require('helmet')
//for parsing tokens and session cookies
var cookieParser= require('cookie-parser')
//assigning express functionaities as a global variable
const app = express()
//assigning cors functionaities as a global variable
app.use(cors())
//headers that will be sent to the browser
app.use((req, res, next) => {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Credentials', true);
res.setHeader('Access-Control-Allow-Methods', ['PATCH', 'POST', 'GET', 'DELETE', 'PUT']);
res.setHeader('Access-Control-Allow-Headers' , '*')
res.setHeader('Access-Control-Expose-Headers' ,'content-type')
next();
});
//body parser for form data
app.use(bodyParser.json())
app.use(bodyParser.json({ type: 'application/json' }))
app.use(bodyParser.urlencoded({ extended: true }))
app.use(cookieParser())
// secure apps by setting various HTTP headers
app.use(helmet())
// enable CORS - Cross Origin Resource Sharing
app.options('*', cors());
//fetching index from react
app.use(express.static('./build/'));
app.use(express.static(path.join(__dirname, './client/build')));
app.get('*', (req, res) => {
res.send(express.static(path.join(__dirname, './client/build/index.html'))) ;
});
//giving the functionalities of users.js to a variable users to be used later on
var Users = require('./routes/Users')
//using the functions assigned for users
app.use('/users', Users)
//exporting the app file
module.exports = app
this is the routes file which contains my controller:
const express = require('express')
//setting a users variable to be used as a router instead of app post , get ..
const users = express.Router()
//cross oigin resource sharing
const cors = require('cors')
//jwt for user login authentication
const jwt = require('jsonwebtoken')
//bcrypt for password encryption and decryption
const bcrypt = require('bcrypt')
//using te user model
const User = require('../model/user')
//setting users as cros origin functionalities
users.use(cors())
//privat key or jwt encryption and decryption
process.env.SECRET_KEY = 'q1w2e3r4t5y6u7i8o9p0o9i8u7y6t5r4e3w2q1'
//main signup function , exported
users.post('/signup', (req, res) => {
//setting a new user object to be manipulated and inserted to db
//data taken from react client side
const today = new Date()
const userData = {
username : req.body.username,
first_name: req.body.first_name,
last_name: req.body.last_name,
email: req.body.email,
password: req.body.password,
created: today
}
//a function from sequelize , a wrapper for later on functions
//searches if the username is found or not
User.findOne({
where: {
//searching in the whhole db for this user name
username: req.body.username
}
})
// encrypting the password using bcrypt encryption function
//bcrypt uses a hash function to encrypt the user given password
//will not reach this part if user is duplicated
.then(user => {
if (!user) {
//hashing the password , 10 is a number for permutations 2pwr10 = a certain string built in
bcrypt.hash(req.body.password, 10, (err, hash) => {
userData.password = hash
//creating a user with the given data
User.create(userData)
//send the username to the response tab in console
.then(user => {
res.json({ status: user.username + ' '+ 'Registered!' })
})
//any error will be consoled here
.catch(err => {
res.send('error: ' + err)
})
})
} else {
//will reach if username is found , User.findOne
res.json({ error: 'User already exists' })
}
})
.catch(err => {
res.send('error: ' + err)
})
})
//main login functionality
users.post('/login', (req, res) => {
///searches for username in db at first
User.findOne({
where: {
username: req.body.username
}
})
//if the user is found , it compared the password with the given password
//it compared it the encrypted pass in the db
//and decrypts it to compare
.then(user => {
if (user) {
//if user name is found the deryption starts here
if (bcrypt.compareSync(req.body.password, user.password)) {
//each user is given a certain jwt token for authentication
//jwt.sign , Synchronously sign the given payload into a JSON Web Token string payload
//secret key provided above
//token is assigned using the front end whuck sends it with the request
let token = jwt.sign(user.dataValues, process.env.SECRET_KEY, {
expiresIn: 1440
})
//send token to local storage of the browser that checks it
res.send(token)
}
} else {
//reaches here if user isnt found
res.status(400).json({ error: 'User does not exist' })
}
})
//catches any error from the above blocks
.catch(err => {
res.status(400).json({ error: err })
})
})
users.get('/profile', (req, res) => {
//Synchronously verify given token using a secret or a public key to get a decoded token token -
// JWT string to verify secretOrPublicKey - Either the secret for HMAC algorithms,
//or the PEM encoded public key for RSA and ECDSA.
// [options] - Options for the verification returns - The decoded token.
var decoded = jwt.verify(req.headers['authorization'], process.env.SECRET_KEY)
//searches for user
User.findOne({
//decode user id and jwt
where: {
id: decoded.id
}
})
//if true, user is sent as a json object to browser
.then(user => {
if (user) {
console.log(user)
res.json(user)
} else {
//if false , send this response
res.send('User does not exist')
}
})
.catch(err => {
res.send('error: ' + err)
})
})
module.exports = users
react front end end-points:
import axios from 'axios'
import jwt_decode from 'jwt-decode'
//the signup endpoint sent from front end and interpreted by the browser
//route is an api called users , exported from server side
//posting user data as in server
//sending a response if true
export const signup = newUser => {
return axios
.post('users/signup', {
username : newUser.username,
first_name: newUser.first_name,
last_name: newUser.last_name,
email: newUser.email,
password: newUser.password
})
.then(response => {
console.log('Registered')
})
}
//login end point
//using username and password , using the decoded id
export const login = async user => {
try {
const response = await axios
.post('users/login', {
username: user.username,
password: user.password
});
localStorage.setItem('usertoken', response.data);
return response.data;
}
catch (err) {
console.log(err);
}
}
Login react page
import React, { Component } from 'react'
import { withRouter} from 'react-router-dom';
import { login } from './api-user'
class Login extends Component {
constructor() {
super()
this.state = {
username: '',
password: '',
errors: {}
}
this.onChange = this.onChange.bind(this)
this.onSubmit = this.onSubmit.bind(this)
}
onChange(e) {
this.setState({ [e.target.name]: e.target.value })
}
// parseJwt(token) {
// if (!token) { return; }
// const base64Url = token.split('.')[1];
// const base64 = base64Url.replace('-', '+').replace('_', '/');
// return JSON.parse(window.atob(base64));
// }
onSubmit(e) {
e.preventDefault()
const user = {
username:this.state.username,
password: this.state.password
}
login(user).then(res => {
if (res) {
this.props.history.push(`/profile`)
}
})
localStorage.setItem('username',
JSON.stringify(this.state.username))
}
render() {
return (
<div className="container">
<div className="row">
<div className="col-md-6 mt-5 mx-auto">
<form noValidate onSubmit={this.onSubmit}>
<h1 className="h3 mb-3 font-weight-normal">Please sign in</h1>
<div className="form-group">
<label htmlFor="text">Username</label>
<input
type="text"
className="form-control"
name="username"
placeholder="Enter username"
value={this.state.username}
onChange={this.onChange}
/>
</div>
<div className="form-group">
<label htmlFor="password">Password</label>
<input
type="password"
className="form-control"
name="password"
placeholder="Password"
value={this.state.password}
onChange={this.onChange}
/>
</div>
<button
type="submit"
className="btn btn-lg btn-primary btn-block"
>
Sign in
</button>
</form>
</div>
</div>
</div>
)
}
}
signup react page
mport React, { Component } from 'react'
import { signup } from './api-user'
class SignUp extends Component {
constructor() {
super()
this.state = {
username:'',
first_name: '',
last_name: '',
email: '',
password: '',
errors: {}
}
this.onChange = this.onChange.bind(this)
this.onSubmit = this.onSubmit.bind(this)
}
onChange(e) {
this.setState({ [e.target.name]: e.target.value })
}
onSubmit(e) {
e.preventDefault()
const newUser = {
username: this.state.username,
first_name: this.state.first_name,
last_name: this.state.last_name,
email: this.state.email,
password: this.state.password
}
signup(newUser).then(res => {
this.props.history.push(`/login`)
})
}
render() {
return (
<div className="container">
<div className="row">
<div className="col-md-6 mt-5 mx-auto">
<form noValidate onSubmit={this.onSubmit}>
<h1 className="h3 mb-3 font-weight-normal">Register</h1>
<div className="form-group">
<label htmlFor="username">User Name</label>
<input
type="text"
className="form-control"
name="username"
placeholder="Enter your username"
value={this.state.username}
onChange={this.onChange}
/>
</div>
<div className="form-group">
<label htmlFor="first_name">First name</label>
<input
type="text"
className="form-control"
name="first_name"
placeholder="Enter your first name"
value={this.state.first_name}
onChange={this.onChange}
/>
</div>
<div className="form-group">
<label htmlFor="last_name">Last name</label>
<input
type="text"
className="form-control"
name="last_name"
placeholder="Enter your lastname name"
value={this.state.last_name}
onChange={this.onChange}
/>
</div>
<div className="form-group">
<label htmlFor="email">Email address</label>
<input
type="email"
className="form-control"
name="email"
placeholder="Enter email"
value={this.state.email}
onChange={this.onChange}
/>
</div>
<div className="form-group">
<label htmlFor="password">Password</label>
<input
type="password"
className="form-control"
name="password"
placeholder="Password"
value={this.state.password}
onChange={this.onChange}
/>
</div>
<button
type="submit"
className="btn btn-lg btn-primary btn-block"
>
Register!
</button>
</form>
</div>
</div>
</div>
)
}
}
export default SignUp
please i asked this question before but no one helped me , some fellows gave me the logic but without the ode , i really in need of some code , what should i do ? new table to record signins ? how to connect my already found user table and if i want a new access_token table ? or should i do a flag in user table that changes (i dont know how , i just have the ideas )
From what I understand from your question is that you don't want your user to open the login page or the signup page if they are already signed in.
If thats the problem then a simple solution is to redirect the user to homepage/dashboard whenever they try to login if they are already authenticated.
<Route
exact path='/login'
render={props => this.state.isAuth ? <Redirect to='/dashboard' /> : <Login />}
/>

to pass email id to ejs templete

I am trying to get the email to ejs template but not getting it. How to get the same email saved from mongoose to ejs template
First, I click on the mail icon to send mail to that selected email by nodemailer.
mailController.js
router.get('/donation/mail', async (req,res) => {
const donations = await Donation.findOne(({_id: id }), {new: true}, (err, doc) => {
if (err) {
console.log("");
}
console.log(doc);
});
res.redirect('/', {
donations
});
});
form.ejs
<div class="form-group">
<input type="text" class="form-control" placeholder="To" name="to" value="<%= donations.email %>">
</div>
mailModel.js
const donationSchema = new Schema({
email: {
type: String
}
})
module.exports = mongoose.model('donations', donationSchema);
error
GET /donation/mail 500 14.818 ms - 2401
Cannot read property 'email' of null
To get that email id from mongoose to ejs template like image

Node.js / Express.js - Fail to get form data and save to db

I tried to make a registration form and save to database (via mLab cloud db), however after submitting the form, the server fail to get the data and no response either. I am new to MEAN Stack, please advise if I make any stupid mistakes.
Many thanks in advance.
controller.js
const bodyparser = require('body-parser');
const userModel = require('../model/userModel');app.use(bodyparser.urlencoded({extended:true}));
//create acc
app.post('/userSignUp', function(req, res){
if (!req.body) return res.sendStatus(404);
var newUser = new userModel({
username: req.body.username,
password: req.body.password
});
//save user to db
newUser.save(function(err){
if (err) throw err;
// fetch user and test password verification
userModel.findOne({username:req.body.username}, function(err, user){
if (err) throw err;
// test a matching password
user.comparePassword(req.body.password, function(err, isMatch){
if (err) throw err;
console.log(req.body.password, isMatch);
res.render('/index.ejs');
});
});
});});
sign-up form
<!-- Sign up form -->
<div id="signup_form" style="width:50%; margin:0 auto;">
<h1>Sign up</h1>
<form action="userSignUp" method="get">
<input type="text" name="username" placeholder="username">
<input type="password" name="password" placeholder="password">
<span>
<input type="checkbox" name="checkbox">
<label for="checkbox">remember</label>
</span>
<input type="submit" value="Sign Up">
</form>
</div>
userModel
var mongoose = require('mongoose'),
Schema = mongoose.Schema,
bcrypt = require('bcrypt'),
SALT_WORK_FACTOR = 10;
var UserSchema = new Schema({
username: { type: String, required: true, index: { unique: true } },
password: { type: String, required: true }
});
UserSchema.pre('save', function(next) {
var user = this;
// only hash the password if it has been modified (or is new)
if (!user.isModified('password')) return next();
// generate a salt
bcrypt.genSalt(SALT_WORK_FACTOR, function(err, salt) {
if (err) return next(err);
// hash the password using our new salt
bcrypt.hash(user.password, salt, function(err, hash) {
if (err) return next(err);
// override the cleartext password with the hashed one
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);
});
};
module.exports = mongoose.model('User', UserSchema);
Developer Tools
you are using get method method while in express router you are using app.post
use method="post" so that you can get from body.property
<form action="userSignUp" method="post">

bcrypt.compareSync is always returning false

I verified that in my db I am saving the username and hash of the password. I am able to retrieve the name from the db, however when I check the password it always returns false. Not sure what is wrong.
Here is my HTML
<div ng-controller="userController">
<div class=user>
<form name="login_form">
<h2 class>Login</h2>
<h3 class = "login_page">UserName</h3>
<input ng-model="user" type="text" ng-minlength="1" required>
<h3 class = "login_page">Password</h3>
<input ng-model="password" type="password" name="password" ng-minlength="4" required>
<input type="submit" value="Login" ng-click="login()" >
<div ng-if ="login_form.$submitted" ng-messages="login_form.password.$error" style="color:maroon" role="alert">
<div ng-message="minlength">Your field is too short</div>
</div>
<p ng-if="error">Username or login is incorrect</p>
</form>
</div>
<div class=user>
<form name = "register_form">
<h2 class>Register</h2>
<h3 class = "login_page">UserName</h3>
<input ng-model="reg.name" type="text" required>
<h3 class = "login_page">Password</h3>
<input ng-model="reg.password" type="password">
<input type="submit" value="Register" ng-click="register()" required >
<div ng-if ="login_form.$submitted" ng-messages="login_form.password.$error" style="color:maroon" role="alert">
<div ng-message="minlength">Your field is too short</div>
</div>
<p ng-if="duplicate">That user name is taken, please choose another</p>
<p ng-if="correct">Registration Succesfull</p>
</form>
</div>
</div>
Here is my controller on the server side
var mongoose = require('mongoose'),
Todo = mongoose.model('Todo');
Login = mongoose.model('Login');
var bcrypt = require('bcrypt');
var name = ""
module.exports = (function(){
return {
save_name:function(req, res){
req.session.user = req.body.user
Login.findOne({name: req.body.user},
function(err, user) {
if(user){
console.log(user.password);
console.log( bcrypt.compareSync(req.body.password, user.password));
res.json({'error': false});
}else {
res.json({'error': true});
}
})
}, //end of save name method
register:function(req, res){
bcrypt.hashSync(req.body.password, bcrypt.genSaltSync(8));
login = new Login({
name:req.body.user,
password: bcrypt.genSaltSync(8)
})
login.save(function(err){
if(err){
res.json({'error': true});
} else {
res.json({'sucess': true})
}
})
} // end of register user function
}
})();
You're saving a generated salt as the password instead of the actual hash itself. Also, explicitly calling genSalt*() is unnecessary. Lastly, you really should use the async functions instead, to avoid unnecessarily blocking the event loop. So with all of this in mind, you may end up with something like:
module.exports = {
save_name: function(req, res) {
req.session.user = req.body.user;
Login.findOne({ name: req.body.user },
function(err, user) {
if (err)
return res.json({ error: true });
bcrypt.compare(req.body.password,
user.password,
function(err, valid) {
res.json({ error: !!(err || !valid) });
});
});
}, // end of save name method
register: function(req, res) {
bcrypt.hash(req.body.password, 8, function(err, hash) {
if (err)
return res.json({ error: true });
login = new Login({
name: req.body.user,
password: hash
})
login.save(function(err) {
res.json({ error: !!err });
})
});
} // end of register user function
};
Despite other answers, if it is still not resolving your issue. Try by applying the toString() when passing the password upon login like this.
req.body.password.toString();
The immediate cause of your bug is in register you should be using bcrypt.hashSync(myPlaintextPassword, saltRounds) instead of genSaltSync. Fixing that should make things "work".
However, you need to recode all this to use the async bcrypt APIs or your application will respond very poorly under load (like crippled and unusable, not just "slow"). General rule: no sync calls in a node.js server.

Resources