i got "Error: data and hash arguments required" on my code - node.js

I'm stuck working on bcrypt error "Error: data and hash arguments required"
I have matched the email and password, and I have also run the mysql results and the result is that it was found, but when run in code it is not found.
What am I doing wrong?
export const Login = async(req,res) => {
try {
const user = await Users.findAll({
where:{
email: req.body.email
}
})
const match = await bcrypt.compare(req.body.password, user[0].password);
if(!match) return res.status(400).json({msg:"Wrong Password!"});
const userId = user[0].id;
const name = user[0].name;
const email = user[0].email;
const accessToken = jwt.sign({userId, name, email}, process.env.ACCESS_TOKEN_SECRET,{
expiresIn: '20s'
});
const refreshToken = jwt.sign({userId, name, email}, process.env.REFRESH_TOKEN_SECRET,{
expiresIn: '1d'
});
await Users.update({refresh_token: refreshToken},{
where:{
id: userId
}
});
res.cookie('refreshToken', refreshToken,{
httpOnly: true,
maxAge: 24 * 60 * 60 * 1000,
// digunakan jika menggunakan https
// kali ini tidak digunakan karna penggunaan di server lokal
// secure: true
})
res.json({ accessToken });
} catch (error) {
console.log(error);
res.status(404).json({msg:"Email tidak ditemukan"});
}
}

Related

nodejs cookie does not save on browser storage

I am building a nodejs application
I want to save cookies using nodejs
it send the cookie to the browser
but it does not save on the browser storage
export const signin = async (req, res, next) => {
try {
const user = await User.findOne({
$or: [{ phone: req.body.phone }, { username: req.body.username }],
});
if (!user) return res.status(401).json({ msg: "Wrong Credentials" });
const isCorrect = bcrypt.compareSync(req.body.password, user.password); // true
if (!isCorrect) return res.status(401).json({ msg: "Wrong Credentials" });
const token = jwt.sign({ id: user._id, role: user.role }, process.env.JWT);
const { password, ...others } = user._doc;
res
.cookie("access_token", token, {
httpOnly: false,
secure: false,
maxAge: 60 * 60 * 24 * 7,
})
.status(200)
.json(others);
} catch (error) {
next(error);
}
};
Frontend
const get = path => {
const new_url = `${BASE_URL}${path}`;
return axios.get(new_url || {}, {
withCredentials: true,
credentials: "include",
});
};

res cookie doesnt update cookies in the browser

I have been trying to set cookies in the browser from nodejs backend trough API with React
and it doesn't want to set them. It's not returning response and it doesn't give me any errors. Does this client.verifytoken function cause the issue? Can you please help?
Nodejs
export const googleAuth = async (req, res) => {
const {tokenId} = req.body
client.verifyIdToken({idToken: tokenId, audience: process.env.GOOGLE_CLIENT_ID}).then((response) => {
const {email_verified, name, email} = response.payload
console.log(response.payload)
if (email_verified) {
Users.findOne({where: {email: email}}).then(user => {
if (user) {
try {
const userId = user.id
console.log('user id', userId)
const refreshToken = jwt.sign({userId}, process.env.REFRESH_TOKEN_SECRET, {expiresIn: '1d'})
Users.update({refreshToken: refreshToken}, {where: {id: userId}})
res.cookie('refreshToken', refreshToken, {
httpOnly: false,
maxAge: 24 * 60 * 60 * 1000,
});
} catch (err) {
console.log(err)
}
} else {
try {
const salt = bcrypt.genSaltSync(2);
const hashPassword = bcrypt.hashSync(email + process.env.ACCESS_TOKEN_SECRET, salt);
const refreshToken = jwt.sign({email}, process.env.REFRESH_TOKEN_SECRET, {expiresIn: '1d'})
console.log('refresh token', refreshToken)
Users.create({
name: name,
email: email,
password: hashPassword,
refresh_token: refreshToken,
verified: true
})
res.cookie('refreshToken', refreshToken, {
httpOnly: true,
maxAge: 24 * 60 * 60 * 1000,
});
} catch (err) {
console.log(err)
}
}
})
}
})
}
Reactjs
const responseSuccessGoogle = async (response) => {
try {
console.log(response)
let result = await axios.post('http://localhost:5000/google-login', {tokenId: response.tokenId},{withCredentials:true})
setAuth(result.data != null)
navigate('/profile')
console.log(result.data)
} catch (error) {
console.log(error)
}
}
res.cookie() doesn't send the response, but only sets the cookie in response causing halt state in your case. You need to send response back either via res.send() or res.end(). You should also send a proper response with error code back to client instead of logging it only, as this would also halt the request. Following code should send response with empty body and send response with error code 500 in case of error.
export const googleAuth = async (req, res) => {
const {tokenId} = req.body
client.verifyIdToken({idToken: tokenId, audience: process.env.GOOGLE_CLIENT_ID}).then((response) => {
const {email_verified, name, email} = response.payload
console.log(response.payload)
if (email_verified) {
Users.findOne({where: {email: email}}).then(user => {
if (user) {
try {
const userId = user.id
console.log('user id', userId)
const refreshToken = jwt.sign({userId}, process.env.REFRESH_TOKEN_SECRET, {expiresIn: '1d'})
Users.update({refreshToken: refreshToken}, {where: {id: userId}})
res.cookie('refreshToken', refreshToken, {
httpOnly: false,
maxAge: 24 * 60 * 60 * 1000,
});
res.send();
} catch (err) {
console.log(err)
res.status(500).send()
}
} else {
try {
const salt = bcrypt.genSaltSync(2);
const hashPassword = bcrypt.hashSync(email + process.env.ACCESS_TOKEN_SECRET, salt);
const refreshToken = jwt.sign({email}, process.env.REFRESH_TOKEN_SECRET, {expiresIn: '1d'})
console.log('refresh token', refreshToken)
Users.create({
name: name,
email: email,
password: hashPassword,
refresh_token: refreshToken,
verified: true
})
res.cookie('refreshToken', refreshToken, {
httpOnly: true,
maxAge: 24 * 60 * 60 * 1000,
});
res.send();
} catch (err) {
console.log(err)
res.status(500).send()
}
}
})
}
})
}

multiple responses Cannot set headers after they are sent to the client

I wrote the following signup function.
It works fine with Postman but when I've added the code between "//send the cookie with the token" and "// end", I got this error message : "(node:11748) UnhandledPromiseRejectionWarning: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client".
From what I saw here in stackoverflow, this error occurs because of multilple res. but I can't find how to rearrange the code so that I avoid this error.
exports.signup = async(req, res) => {
const { firstName, familyName, email, password, role } = req.body;
console.log("image", req.file);
try {
const user = await User.findOne({ attributes: ['email'], where: { email: email } });
if (user) {
fs.unlinkSync(req.file.path);
return res.status(409).send('This email already exists!');
} else {
const hashPass = await bcrypt.hash(password, 10);
const userObject = {
firstName: firstName,
familyName: familyName,
email: email,
password: hashPass,
role: role,
photoUrl: req.file ? `${req.protocol}://${req.get('host')}/images/${req.file.filename}` : null,
};
console.log("photo", userObject.photoUrl);
console.log("userObject", userObject);
const createdUser = await User.create(userObject);
const newToken = jwt.sign({ userId: user.id },
process.env.COOKIE_KEY, { expiresIn: "24h" }
);
const newCookie = { token: newToken, userId: createdUser.id };
const cryptedToken = cryptojs.AES.encrypt(JSON.stringify(newCookie), process.env.COOKIE_KEY).toString();
res.cookie('snToken', cryptedToken, {
httpOnly: true,
maxAge: 86400000 // 24h
});
res.status(200).send({ message: 'The user is successfully connected!', data: createdUser, cryptedToken: cryptedToken });
}
} catch (error) {
return res.status(500).send({ error: 'An error has occured while trying to sign up!' });
}
}
It's not related to "multiple request". In a successful case (also on error), you wrote status more than once.
for example in a successful case:
first you returned 201 after creating the user (which returned a response to the client)
res.status(201).send({message: User ${req.userObject.firstName} ${req.userObject.familyName} was registred }, createdUser);
and then
and here you tried to send another response at the end of the request
res.status(200).send({ message: 'The user is successfully connected!', data: user, cryptedToken: cryptedToken });
and you got the right error Cannot set headers after they are sent to the client, since it already returned 201 after Creation.
try to do something like:
try {
const hashPass = await bcrypt.hash(password, 10);
const userObject = { firstName: firstName,
familyName: familyName,
email: email,
password: hashPass,
role: role,
photoUrl: req.file ? `${req.protocol}://${req.get('host')}/images/${req.file.filename}` : null,
};
console.log("photo", userObject.photoUrl);
console.log("userObject", userObject);
const createdUser = await User.create(userObject);
//send the cookie with the token
const newToken = jwt.sign({ userId: user.id },
process.env.COOKIE_KEY, { expiresIn: "24h" }
);
const newCookie = { token: newToken, userId: user.id };
const cryptedToken = cryptojs.AES.encrypt(JSON.stringify(newCookie), process.env.COOKIE_KEY).toString();
res.cookie('snToken', cryptedToken, {
httpOnly: true,
maxAge: 86400000 // 24h
});
res.status(200).send({ message: 'The user is successfully connected!', data: user, cryptedToken: cryptedToken });
}
catch (error) {
return res.status(500).send({ error: 'An error has occured while trying to sign up!' });
}

Mongoose return "new ObjectId("//id")" instead of just the id

I am trying to do my login function (I am using bcrypt and jsonwebtoken) the problem is that console.log (user._id) returns me "new ObjectId (" 6148f043ebbaa0ab41ac8499 ")" instead of just "6148f043ebbaa0ab41ac8499" , which would be easier for the creation of the token.
module.exports.login = async (req, res) => {
const { email, password } = req.body;
// Compare the req.body.password to the hashed password in DB
const user = await UserModel.findOne({ email: email });
const match = await bcrypt.compare(password, user.password);
if (match) {
try {
const user = await UserModel.findOne({ email: email });
console.log(user._id);
// Assign a token
const token = jwt.sign({ userId: user._id }, process.env.LOGIN_TOKEN, {
expiresIn: "1h",
});
console.log(token);
res.cookie("jwt", token, { httpOnly: true});
res.status(200).json({ user: user._id });
} catch (err) {
res.status(500).json(err);
}
} else {
res.status(500).json({ message: "error!!!" });
}
};
How to fix this please?
That is a normal behaviour. Since you got an ObjectId, you can convert it to a string by calling the toHexString() method on it. I have also modified the code to check for an undefined user, and removed the extra call to find a user since you already did in the previous line. Please see updated code:
module.exports.login = async (req, res) => {
const { email, password } = req.body;
const user = await UserModel.findOne({ email: email });
if (!user) {
return res.status(400).json({ message: "Unauthorised"});
}
// Compare the req.body.password to the hashed password in DB
const match = await bcrypt.compare(password, user.password);
if (match) {
try {
// Convert user id (ObjectId) to a string
const userId = user._id.toHexString();
// Now user id is a string
console.log(userId);
// Assign a token
const token = jwt.sign({ userId }, process.env.LOGIN_TOKEN, {
expiresIn: "1h",
});
console.log(token);
res.cookie("jwt", token, { httpOnly: true});
res.status(200).json({ user });
} catch (err) {
res.status(500).json(err);
}
} else {
res.status(400).json({ message: "Unauthorised" });
}
};
use the following
const id = _id.toHexString();

bcrypt-nodejs compare function always return false

I'm having problem with bcrypt-nodejs' compare function.
The compare function is returning the false value even the password is the right one.
I've tried everything I could and I don't know the what is wrong with my code.
My Folder Structure
src
-config
-config.js
-controller
-AuthenticationController.js
-models
-index.js
-User.js
-policies
-AuthenticationControllerPolicy.js
app.js
routes.js
package.json
I think the problem is with the User.js in models folder.
User.js
const Promise = require('bluebird')
const bcrypt = Promise.promisifyAll(require('bcrypt-nodejs'))
function hashPassword (user, options) {
const SALT_FACTOR = 8
if (!user.changed('password')) {
return
}
return bcrypt
.genSaltAsync(SALT_FACTOR)
.then(salt => bcrypt.hashAsync(user.password, salt, null))
.then(hash => {
user.setDataValue('password', hash)
})
}
module.exports = (sequelize, DataTypes) => {
const User = sequelize.define('User', {
email: {
type: DataTypes.STRING,
unique: true
},
password: DataTypes.STRING
}, {
hooks: {
beforeCreate: hashPassword,
beforeUpdate: hashPassword,
beforeSave: hashPassword
}
})
User.prototype.comparePassword = function (password) {
return bcrypt.compareAsync(password, this.password)
}
User.associate = function (models) {
}
return User
}
AuthenticationController.js
const {User} = require('../models')
const jwt = require('jsonwebtoken')
const config = require('../config/config')
function jwtSignUser (user) {
const ONE_WEEK = 60 * 60 * 24 * 7
return jwt.sign(user, config.authentication.jwtSecret, {
expiresIn: ONE_WEEK
})
}
module.exports = {
async register (req, res) {
try {
const user = await User.create(req.body)
const userJson = user.toJSON()
res.send({
user: userJson
})
} catch (err) {
res.status(400).send({
error: 'This email account is already in use.'
})
}
},
async login (req, res) {
try {
const {email, password} = req.body
const user = await User.findOne({
where: {
email: email
}
})
console.log('user BEFORE', user)
if (!user) {
console.log('!user')
return res.status(403).send({
error: 'The login information was incorrect'
})
}
console.log('user AFTER', user)
const isPasswordValid = await user.comparePassword(password)
console.log('isPasswordValid BEFORE : ', isPasswordValid)
if (!isPasswordValid) {
console.log('isPasswordValid AFTER : ', isPasswordValid)
return res.status(403).send({
error: 'The login information was incorrect'
})
}
const userJson = user.toJSON()
res.send({
user: userJson,
token: jwtSignUser(userJson)
})
} catch (err) {
res.status(500).send({
error: 'An error has occured trying to log in'
})
}
}
}
route.js
const AuthenticationController = require('./controller/AuthenticationController')
const AuthenticationControllerPolicy = require('./policies/AuthenticationControllerPolicy')
module.exports = (app) => {
app.post('/register',
AuthenticationControllerPolicy.register,
AuthenticationController.register)
app.post('/login',
AuthenticationController.login)
}
You can also check the repo if you want.
GitHubRepo
The usage of bcrypt-nodejs appears to be correct. I would verify that both the password coming in and the hash in the database are what you expect them to be (particularly inside the comparePassword function) to rule out if it's a data issue or not.

Resources