I have an api for reseting password. This api checks if there is a user with the entered phone number and if there is any it checks whether the inputs like dob, nationality and idnumber are also correct then if they are it then generates a new password for the user. However am using many if statements . So my question is whether this is practical or i should change my syntax for performance sake of the app and general good practices for writing standard code.
I have attached my code below with bunch of if statements lol
const asyncHandler = require("express-async-handler");
const User = require("../../models/user")
const bcrypt = require("bcrypt");
const jwt = require("jsonwebtoken");
const generateStrongPassword = require('../../utils/generateStrongPassword');
const resetPassword = asyncHandler(async (req, res) => {
const { phonenumber, fullname, nationality, nationalidnumber, dateofbirth } = req.body;
const user = await User.findOne({ phonenumber: phonenumber });
if (user) {
if (fullname != user.fullname) {
throw new Error('incorrect fullname');
}
if (nationality != user.nationality) {
throw new Error('incorrect nationality');
}
if (nationalidnumber != user.nationalidnumber) {
throw new Error('incorrect national id number ');
}
if (dateofbirth != user.dateofbirth) {
throw new Error('incorrect date of birth');
}
// if all validations are passed then
// generate new password
const newrawpassword = await generateStrongPassword();
console.log(newrawpassword);
// harsh the password
const salt = await bcrypt.genSalt(10);
const newHashedPassword = await bcrypt.hash(newrawpassword, salt);
const resetPassword = await User.updateOne({ $set: { password: newHashedPassword } })
res.json('password successfully reseted')
} else {
throw new Error('No account has that phone number');
}
});
module.exports = resetPassword;
Related
My code above runs without any errors but the new password isn't saved.
I've been following the bcrypt docs, a blog post and a video and think the three different sources have resulted in my missing something critical.
Any ideas why the new password isn't being saved?
module.exports.submitNewPassword = async (req, res) => {
const slidedHeaderToken = req.headers.referer.slice(-40);
const artist = await Artist.find({ resetPasswordToken: slidedHeaderToken, resetPasswordExpires: { $gt: Date.now() } });
if (!artist) {
console.log("Artist doesn't exist");
} else {
const hashedPassword = async (pw) => {
bcrypt.hash(req.body.password, 12)
}
hashedPassword()
artist.password = hashedPassword;
resetPasswordToken = null;
resetPasswordExpires = null;
console.log("Successfully resubmitted password");
res.redirect('login');
}
}
You should call await artist.save() function after you set some fields
artists is an array of all the artists matching the parameters in the call to .find(). If you just want to find one, use .findOne().
Then after you modify it, use .save() to save it back to the database.
module.exports.submitNewPassword = async (req, res) => {
const slidedHeaderToken = req.headers.referer.slice(-40);
const artist = await Artist.findOne({ resetPasswordToken: slidedHeaderToken, resetPasswordExpires: { $gt: Date.now() } });
if (!artist) {
console.log("Artist doesn't exist");
} else {
const hashedPassword = async (pw) => {
bcrypt.hash(req.body.password, 12)
}
hashedPassword()
artist.password = hashedPassword;
await artist.save();
resetPasswordToken = null;
resetPasswordExpires = null;
console.log("Successfully resubmitted password");
res.redirect('login');
}
}
See the Mongoose tutorial here.
When you make a change to the data using FindOne, you need to save it.
await yourVariable.save()
I have been struggling with this for hours and have tried a lot of different variations I have found around the web and also on stack overflow but I keep getting stuck on the same thing.
This is my registration code:
// REGISTER USER
app.post("/register", async (request, response) => {
const saltRounds = 10;
const emailAddress = request.body.emailAddress;
const password = await bcrypt.hash(request.body.password, saltRounds);
console.log(password)
// CHECK IF A USER EXISTS
const sqlSearch = "SELECT * FROM users WHERE emailAddress = ?"
const search_query = mysql.format(sqlSearch, [emailAddress])
// INSERT NEW USER
const sqlInsert = "INSERT INTO users (emailAddress, password) VALUES (?,?)"
const insert_query = mysql.format(sqlInsert, [emailAddress, password])
await usersDB.query(search_query, async (err, result) => {
if (err) throw (err)
if (result.length != 0) {
console.log("------> User already exists")
response.send("exists")
} else {
await usersDB.query(insert_query, (err, result) => {
if (err) throw (err)
response.send("created")
})
}
})
})
This is my login code:
// LOGIN (AUTHENTICATE USER)
app.post("/login", async (request, response) => {
const emailAddress = request.body.emailAddress
const password = request.body.password
const sqlSearch = "SELECT * FROM users WHERE emailAddress = ?"
const search_query = mysql.format(sqlSearch, [emailAddress])
await usersDB.query(search_query, async (err, result) => {
if (err) throw (err)
if (result.length == 0) {
console.log("--------> User does not exist")
response.sendStatus(404)
} else {
// Get the hashed password from result
const hashedPassword = result[0].Password
await bcrypt.compare(password, hashedPassword, function(err, result) {
if (result) {
console.log("---------> Login Successful")
response.send(`${emailAddress} is logged in!`)
} else {
console.log("---------> Password Incorrect")
console.log(password)
console.log(hashedPassword)
response.send("Password incorrect!")
}
});
}
})
})
I don't really understand what is going wrong in the compare considering the hashes are the same, I also tried pulling the salt rounds out and declaring them as a variable as you can see, this was recommended on another answer. I have changed the compare await in several different ways but they all give the same result.
I did also check the typeof on each var and they are all strings as they need to be.
My output:
The first hash you see is what is going into the database, the password being "test" and the second hash is from the compare statement along with the plaintext being shown.
$2b$10$wXGSrneIiovWHG7wk6a0BOIXwhzelTlCcxeoLsVJ8Au4iiOcoBBhe
---------> Password Incorrect
test
$2b$10$wXGSrneIiovWHG7wk6a0BOIXwhzelTlCcxeoLsVJ8Au4iiOcoBBhe
Any help would be greatly appreciated.
Note: The password column in my DB is a VARCHAR(255)
You can make a 2 seperate function for achieve the bcrypt functions. Here is the helper file which holds the bcrypt functions
const logger = require('./logger');
const bcrypt = require('bcrypt');
const encryptUtil = {};
// It make a hash password
encryptUtil.oneWayEncrypt = async (text) => {
try {
const salt = await bcrypt.genSalt(parseInt(process.env.SALT_ROUND, 10));
const encoded = await bcrypt.hash(text, salt);
return { encoded, salt };
} catch (err) {
logger.error('[ERROR] From oneWayEncrypt in encryptUtils', err);
throw err;
}
};
// It will validate plain text with the hashed one
encryptUtil.validateBcryptHash = async (text, hash) => {
try {
const isExactMatch = await bcrypt.compare(text, hash);
return isExactMatch;
} catch (err) {
logger.error('[ERROR] From validateBcryptHash in encryptUtils', err);
throw err;
}
};
module.exports = encryptUtil;
Here is the usecase of that function in signup and login
const encryptUtil = require('../../../helper/encryptUtil');
const logger = require('../../../helper/logger');
const jwt = require('../../../helper/jwt');
const userUtils = {};
userUtils.signUp = async (obj) => {
try {
const { name, password } = obj;
const email = obj.email.toLowerCase();
const condition = { email };
const querying = {
attributes: ['id', 'name', 'email''],
where: { email },
};
const isEmailExist = await Model.user.findOne(querying);
if (isEmailExist) {
const errorObj = { code: 400, error: l10n.t('ERR_EMAIL_ALREADY_EXIST') };
throw errorObj;
}
const { encoded: encPassword } = await encryptUtil.oneWayEncrypt(password);
const insertObj = {
name,
email,
password: encPassword,
};
const result = await Model.user.create(insertObj);
const userId = result.id;
const token = jwt.getAuthToken({ userId });
return { token, msg: l10n.t('MSG_SIGNUP_SUCCESS'), user: { name, email, userId } };
} catch (error) {
logger.error('[ERROR] From signUp in userUtils', error);
throw error;
}
};
userUtils.login = async (obj) => {
try {
const { password } = obj;
const email = obj.email.toLowerCase();
const querying = {
attributes: ['id', 'name', 'email', 'password'],
where: { email },
};
const user = await Model.user.findOne(querying);
if (!user) {
const errorObj = { code: 400, error: l10n.t('ERR_CREDENTIAL_NOT_MATCHED') };
throw errorObj;
}
// Here it validates the simple text with hashed text which store in a dbatabase
const isExactMatch = await encryptUtil.validateBcryptHash(password, user.password);
if (!isExactMatch) {
const errorObj = { code: 400, error: l10n.t('ERR_CREDENTIAL_NOT_MATCHED') };
throw errorObj;
}
const token = jwt.getAuthToken({ userId: user.id });
const result = {
token,
user: {
userId: user.id,
name: user.name,
email: user.email,
};
return result;
} catch (error) {
logger.error('[ERROR] From login in userUtils', error);
throw error;
}
};
module.exports = userUtils;
After importing the AWS module I go ahead and declare the cognitoidentityserviceprovider variable:
let AWS = require("aws-sdk");
let AWS_REGION = "us-east-1";
let USER_POOL_ID = 'us-east-1_4RQvUuPkX';
let AWS_COGNITO_CLIENT_ID = 'l703om838lkem323m04tiparls';
let AWS_COGNITO_IDENTITY_POOL_ID = 'us-east-1:112e122-bdd5-1234-983b-0afff8de2b3f';
AWS.config.update({
region: AWS_REGION,
});
let cognitoidentityserviceprovider = new AWS.CognitoIdentityServiceProvider({
apiVersion: "2016-04-19",
region: AWS_REGION
});
After the initial configuration is set I am ready to define the create_user function that I can use to create a new Cognito user supplying this functions with the user's email and password:
async function create_user(email, password) {
let params = {
UserPoolId: USER_POOL_ID,
Username: email,
DesiredDeliveryMediums: ["EMAIL"],
TemporaryPassword: password,
UserAttributes: [
{
Name: "email",
Value: email
},
{
Name: "email_verified",
Value: "true"
}
]
};
return await cognitoidentityserviceprovider.adminCreateUser(params).promise();
}
Next, I define another function confirm_user which I am going to use to confirm the new user by setting the user's password:
async function confirm_user(sub_id, password) {
let params = {
Password: password,
UserPoolId: USER_POOL_ID,
Username: sub_id,
Permanent: true
};
return await cognitoidentityserviceprovider.adminSetUserPassword(params).promise();
}
With both create_user and confirm_user functions defined I can now create a new Cognito user and confirm it on spot without a need for the user to confirm the sign-up process:
async function main(email, password) {
let user_data = await create_user(email, password);
let sub_id = user_data.User.Attributes[0].Value;
let confirm_data = await confirm_user(sub_id, password);
}
let EMAIL = 'foo#bar.com';
let PASSWORD = 'MY_PASSWORD';
main(EMAIL, PASSWORD)
Since I want to get the user's Identity Pool ID (as identityID variable here) I need to define a third function that I name as authenticate_user(email, password):
const AmazonCognitoIdentity = require('amazon-cognito-identity-js');
async function authenticate_user(email, password) {
var authenticationData = {
Username: email,
Password: password,
};
let auth_details = new AmazonCognitoIdentity.AuthenticationDetails(authenticationData);
let poolData = {
UserPoolId : USER_POOL_ID,
ClientId : AWS_COGNITO_CLIENT_ID
};
let pool = new AmazonCognitoIdentity.CognitoUserPool(poolData);
let userData = {
Username: email,
Pool: pool
};
let cognito_user = new AmazonCognitoIdentity.CognitoUser(userData);
let jwt_token;
await new Promise((resolve) => {
cognito_user.authenticateUser(auth_details, {
onSuccess: (result) => {
jwt_token = result.getIdToken().getJwtToken();
return resolve(jwt_token);
},
onFailure: (err) => {
return resolve(err.message || JSON.stringify(err) );
},
});
});
let logins = {};
logins["cognito-idp." + AWS_REGION + ".amazonaws.com/" + USER_POOL_ID] = jwt_token;
let creds = new AWS.CognitoIdentityCredentials({
IdentityPoolId: AWS_COGNITO_IDENTITY_POOL_ID,
Logins: logins
});
let IdentityId;
await new Promise((resolve) => {
creds.get(function(err) {
IdentityId = creds.data.IdentityId;
resolve(IdentityId);
});
})
return IdentityId;
}
Apparently it is a very long and complex way of getting the user's identityID. Is there a simpler way to get the user federated Identity Pool ID? Aside from the complexity, we need to know the user password to get the user's Identity Pool ID. Is there a way to get it without knowing the user's password?
I was following the tutorial from Dev.to. But I stucked on this: bcrypt's compare not working
The code:
const isMatch = await bcrypt.compare(password, user.password);
if (!isMatch){
return res.status(400).json({
msg: "incorrect password"
});
}
Is the string coming from user.password a hash?
bcrypt compares your raw string with a hash. Here's a simple working example that you can run in a node file.
const bcrypt = require('bcrypt');
const bcryptTest = async () => {
try {
const password = 'mypassword';
const userPass = await bcrypt.hash('mypassword', 5);
const isMatch = await bcrypt.compare(password, userPass);
console.log(isMatch) // returns true
} catch (e) {
console.log(e)
}
}
bcryptTest();
Ok, I'm getting an undefined value from a function, I don't know why, I'm trying to get the value of a password hash for insert in the database, but the const that have the function has the value "undefined", so what I should change in my code?
async postCompletedetails(req, res) {
const company = req.params.company;
const name = req.params.name;
const password = req.params.password;
const hashPass = await bcrypt.hash(password, saltRounds, function(err, hash) {
if (err) {
throw err
} else {
console.log(hash)
}
})
if (
company !== undefined &&
name !== undefined &&
password !== undefined
) {
const {
token
} = req.headers;
const decoded = jwt.verify(token, process.env.JWT_ACCOUNT_ACTIVATION);
const id = decoded.id;
const update = await pool.query(
`UPDATE user SET Name_user= '${name}', password= '${hashPass}' WHERE ID_user = ${id}`
);
const incompany = await pool.query(
`INSERT INTO company (Name_company) VALUES ('${company}') `
);
const inrelcompany = await pool.query(
`INSERT INTO rel_company_user (ID_company, ID_user) VALUES (LAST_INSERT_ID(), ${id})`
);
return res.json({
code: 200,
message: "todo bien... todo correcto y yo que me alegro",
hashPass,
password
});
} else {
return res.json({
code: 400,
message: "Bro hiciste algo mal",
});
}
}
When you call bcrypt.hash() and pass in a callback function, no Promise is returned. You can leave off that callback and then your await will work as you expect.
Basically, as is common with a lot of APIs, you can choose between the "old school" callback function approach or the more modern Promise/async model. One or the other, but not both at the same time.