Below is my controller from user registration. Before registering, I would like the getAccountBill method to return the result, because it returned null before using async / await. I have now a this error:
const user = await User.create({
^^^^^
SyntaxError: await is only valid in async function
Controller:
// Register Action
exports.register = (req, res) => {
function getAvailableFunds() {
const availableFunds = 0;
return availableFunds;
}
async function getAccountBill() {
const accountBill = `2222${Math.floor(
Math.random() * 90000000000000000000,
) + 10000000000000000000}`;
try {
const isAccountBill = await Bill.findOne({
where: {
account_bill: accountBill,
},
});
return isAccountBill ? await getAccountBill() : accountBill;
} catch(e) {
console.error(e);
}
}
function getAccountBalanceHistory() {
const accountBalanceHistory = '0,0';
return accountBalanceHistory;
}
function getTodayDate() {
const today = new Date();
return today;
}
User.findOne({
where: { login: req.body.login },
}).then(isUser => {
if (!isUser) {
bcrypt.hash(req.body.password, 10, (err, hash) => {
req.body.password = hash;
const user = await User.create({
login: req.body.login,
password: req.body.password,
name: req.body.name,
surname: req.body.surname,
email: req.body.email,
date_registration: getTodayDate(),
});
const account_bill = await getAccountBill();
const bill = await Bill.create({
id_owner: user.id,
account_bill,
available_funds: getAvailableFunds(),
})
const additional = await Additional.create({
id_owner: user.id,
account_balance_history: getAccountBalanceHistory(),
});
res.status(200).json({ register: true });
}),
);
});
} else {
res.status(400).json({ error: 'User already exists.' });
}
});
};
What is the problem?
Welcom to stackoverflow, try this solution.
The await keyword is only valid inside async functions. If you use it outside of an async function's body, you will get a SyntaxError.
So you must make the change here:
bcrypt.hash(req.body.password, 10, async (err, hash) => { ...
Also, I made corrections to your code to work properly, check it out and happy coding!
function getAvailableFunds() {
const availableFunds = 0;
return availableFunds;
}
async function getAccountBill() {
const accountBill = `2222${Math.floor(
Math.random() * 90000000000000000000,
) + 10000000000000000000}`;
try {
const isAccountBill = await Bill.findOne({
where: {
account_bill: accountBill,
},
});
return isAccountBill ? await getAccountBill() : accountBill;
} catch (e) {
console.error(e);
}
}
function getAccountBalanceHistory() {
const accountBalanceHistory = '0,0';
return accountBalanceHistory;
}
function getTodayDate() {
const today = new Date();
return today;
}
// Register Action
module.exports.register = (req, res) => {
User.findOne({
where: { login: req.body.login },
}).then((isUser) => {
if (!isUser) {
bcrypt.hash(req.body.password, 10, async (err, hash) => {
req.body.password = hash;
const user = await User.create({
login: req.body.login,
password: req.body.password,
name: req.body.name,
surname: req.body.surname,
email: req.body.email,
date_registration: getTodayDate(),
});
const account_bill = await getAccountBill();
const bill = await Bill.create({
id_owner: user.id,
account_bill,
available_funds: getAvailableFunds(),
})
const additional = await Additional.create({
id_owner: user.id,
account_balance_history: getAccountBalanceHistory(),
});
res.status(200).json({ register: true });
});
} else {
res.status(400).json({ error: 'User already exists.' });
}
});
}
I believe it could be fixed by changing this line
bcrypt.hash(req.body.password, 10, (err, hash) => {
to
bcrypt.hash(req.body.password, 10, async (err, hash) => {
Related
I'm using passport strategies for different socialMedia logins and getting the following two errors
InternalOAuthError: Failed to fetch user profile
Cannot set headers after they are sent to the client
I have doubt there somewhere I have returned a callback or response so getting 2nd error but for 1st don't know reasons scope seems to be correct!
strategy code
passport.use(new GoogleStrategy({
clientID: GOOGLE_CLIENT_ID,
clientSecret: GOOGLE_SECRET_KEY,
callbackURL: GOOGLE_CALLBACK_URL
}, async (acessToken, refreshToken, profile, done) => {
await User.findOne({ email: profile._json.email }, async (err, user) => {
if (err) {
console.log("passport.config --> err", err);
done(err, null);
} else if (user) {
if (user.socialType !== "GOOGLE" || user.socialType === null)
done(`LOGIN_CREDENTIALS_WITH_${(user.socialType || "PASSWORD").toUpperCase()}`, false);
else {
done(null, user);
}
} else {
// console.log(profile);
const user = {
email: profile._json.email,
socialId: profile.id,
socialType: "GOOGLE",
firstName: profile.name.givenName,
lastName: profile.name.familyName,
isActive: profile._json.email_verified,
isVerified: profile._json.email_verified,
socialImageUrl: profile._json.picture,
userType: "CUSTOMER"
};
const newUser = new User({ ...user });
const newUserData = await newUser.save();
done(null, newUserData);
}
});
}));
route code:
router.get('/auth/:socialType', customerCtrl.socialTypeLogin);
router.get('/auth/:socialType/callback', customerCtrl.socialTypeLoginCallback);
controller code:
const socialTypeLogin = async (req, res) => {
await customerService.socialTypeLogin(req, res);
};
const socialTypeLoginCallback = async (req,res) => {
await customerService.socialTypeLoginCallback(req,res);
};
service code:
const socialTypeLogin = async (req, res) => {
try {
const socialType = (req.params.socialType || '').toLowerCase();
const GOOGLE_SCOPE = ['email', 'profile'];
const FACEBOOK_SCOPE = ['email'];
let scope = [];
if (socialType === 'google') {
scope = GOOGLE_SCOPE;
} else if (socialType === 'facebook') {
scope = FACEBOOK_SCOPE;
}
let oauthOptions = { scope: scope};
const { returnUrl } = req.query;
if(returnUrl && returnUrl.trim().length !== 0) {
oauthOptions['state'] =JSON.stringify({ returnUrl: returnUrl });
}
passport.authenticate(socialType, oauthOptions)(req, res);
}
catch (error) {
}
}
/**
* #param {string} socialType
*/
const socialTypeLoginCallback = async (req, res) => {
const socialType = (req.params.socialType || '').toLowerCase();
// return new Promise((resolve, reject) => {
try {
passport.authenticate(socialType, async (err, user) => {
let webappRedirectURL = WEBAPP_LOGIN_URL;
try {
const state = req.query.state;
if(state) {
const stateObj = JSON.parse(state);
webappRedirectURL = stateObj.returnUrl;
}
} catch (err1) {
console.log("customer.service --> parsing error",err1);
}
if (err || !user) {
console.log("customer.service --> !user",err);
res.render('oauth-redirect', {
webappRedirectURL: webappRedirectURL,
success: false,
error: err,
timerCounter: 5,
accessToken: undefined
});
}
else {
console.log("customer.service --> Generating Token",user.generateJWT());
res.render('oauth-redirect', {
webappRedirectURL: webappRedirectURL,
success: true,
timerCounter: 5,
accessToken: user.generateJWT(),
error: undefined
});
}
})(req, res);
}
catch (error) {
console.log("customerService.js ==> socialTypeLoginCallback -->",error);
}
};
Thanks for help in advance!
I have doubt there somewhere I have returned a callback or response so getting 2nd error but for 1st don't know reasons scope seems to be correct!
In socialTypeLogin
add line
oauthOptions['session'] = false;
Im getting this error, but I don't know what's wrong with my code, since it works perfectly on Postman, but doesn't when I run it on Node.js or localhost:3000 (which is my client side).
Here's the controller authCtrl.js code:
const Users = require("../models/userModel");
const bcrypt = require("bcrypt");
const jwt = require("jsonwebtoken");
const authCtrl = {
register: async (req, res) => {
try {
const { fullname, username, email, password, gender } = req.body;
let newUserName = username.toLowerCase().replace(/ /g, "");
const user_name = await Users.findOne({ username: newUserName });
if (user_name)
return res.status(400).json({ msg: "This user name already exists." });
const user_email = await Users.findOne({ email });
if (user_email)
return res.status(400).json({ msg: "This email already exists." });
if (password.length < 6)
return res
.status(400)
.json({ msg: "Password must be at least 6 characters." });
const passwordHash = await bcrypt.hash(password, 12);
const newUser = new Users({
fullname,
username: newUserName,
email,
password: passwordHash,
gender,
});
const access_token = createAccessToken({ id: newUser._id });
const refresh_token = createRefreshToken({ id: newUser._id });
res.cookie("refreshtoken", refresh_token, {
httpOnly: true,
path: "http://localhost:5000/api/refresh_token",
maxAge: 30 * 24 * 60 * 60 * 1000, // 30days
});
await newUser.save();
res.json({
msg: "Register Success!",
access_token,
user: {
...newUser._doc,
password: "",
},
});
} catch (err) {
return res.status(500).json({ msg: err.message });
}
},
login: async (req, res) => {
try {
const { email, password } = req.body;
const user = await Users.findOne({ email }).populate(
"followers following",
"avatar username fullname followers following"
);
if (!user)
return res.status(400).json({ msg: "This email does not exist." });
const isMatch = await bcrypt.compare(password, user.password);
if (!isMatch)
return res.status(400).json({ msg: "Password is incorrect." });
const access_token = createAccessToken({ id: user._id });
const refresh_token = createRefreshToken({ id: user._id });
res.cookie("refreshtoken", refresh_token, {
httpOnly: true,
path: "http://localhost:5000/api/refresh_token",
maxAge: 30 * 24 * 60 * 60 * 1000, // 30days
});
res.json({
msg: "Login Success!",
access_token,
user: {
...user._doc,
password: "",
},
});
} catch (err) {
return res.status(500).json({ msg: err.message });
}
},
logout: async (req, res) => {
try {
res.clearCookie("refreshtoken", {
path: "http:/localhost:5000/api/refresh_token",
});
return res.json({ msg: "Logged out!" });
} catch (err) {
return res.status(500).json({ msg: err.message });
}
},
generateAccessToken: async (req, res) => {
try {
const rf_token = req.cookies.refreshtoken;
if (!rf_token)
return res.status(400).json({ msg: "Refresh token failed." });
jwt.verify(
rf_token,
process.env.REFRESH_TOKEN_SECRET,
async (err, result) => {
if (err) return res.status(400).json({ msg: "JWT Verify failed." });
const user = await Users.findById(result.id)
.select("-password")
.populate(
"followers following",
"avatar username fullname followers following"
);
if (!user)
return res.status(400).json({ msg: "This does not exist." });
const access_token = createAccessToken({ id: result.id });
res.json({
access_token,
user,
});
}
);
} catch (err) {
return res.status(500).json({ msg: err.message });
}
},
};
const createAccessToken = (payload) => {
return jwt.sign(payload, process.env.ACCESS_TOKEN_SECRET, {
expiresIn: "1d",
});
};
const createRefreshToken = (payload) => {
return jwt.sign(payload, process.env.REFRESH_TOKEN_SECRET, {
expiresIn: "30d",
});
};
module.exports = authCtrl;
Here's the util fetchData.js (where I use axios to fetch the data):
import axios from "axios";
export const getDataAPI = async (url, token) => {
const res = await axios.get(`http://localhost:5000/api/${url}`, {
headers: { Authorization: token },
});
return res;
};
export const postDataAPI = async (url, post, token) => {
const res = await axios.post(`http://localhost:5000/api/${url}`, post, {
headers: { Authorization: token },
});
return res;
};
export const putDataAPI = async (url, post, token) => {
const res = await axios.put(`http://localhost:5000/api/${url}`, post, {
headers: { Authorization: token },
});
return res;
};
export const patchDataAPI = async (url, post, token) => {
const res = await axios.patch(`http://localhost:5000/api/${url}`, post, {
headers: { Authorization: token },
});
return res;
};
export const deleteDataAPI = async (url, token) => {
const res = await axios.delete(`http://localhost:5000/api/${url}`, {
headers: { Authorization: token },
});
return res;
};
Here's the redux action authAction.js file:
import { GLOBALTYPES } from "./globalTypes";
import { postDataAPI } from "../../utils/fetchData";
export const login = (data) => async (dispatch) => {
try {
dispatch({ type: GLOBALTYPES.ALERT, payload: { loading: true } });
const res = await postDataAPI("login", data);
dispatch({
type: GLOBALTYPES.AUTH,
payload: {
token: res.data.access_token,
user: res.data.user,
},
});
localStorage.setItem("firstLogin", true);
dispatch({
type: GLOBALTYPES.ALERT,
payload: {
success: res.data.msg,
},
});
} catch (err) {
dispatch({
type: GLOBALTYPES.ALERT,
payload: {
error: err.response.data.msg,
},
});
}
};
export const refreshToken = () => async (dispatch) => {
const firstLogin = localStorage.getItem("firstLogin");
if (firstLogin) {
dispatch({ type: GLOBALTYPES.ALERT, payload: { loading: true } });
try {
const res = await postDataAPI("refresh_token");
dispatch({
type: GLOBALTYPES.AUTH,
payload: {
token: res.data.access_token,
user: res.data.user,
},
});
dispatch({ type: GLOBALTYPES.ALERT, payload: {} });
} catch (err) {
dispatch({
type: GLOBALTYPES.ALERT,
payload: {
error: err.response.data.msg,
},
});
}
}
};
As the name states, I keep getting a "Query was already executed" while running Mongoose.find queries. Using '.clone' does not seem to be fixing the issue...
My calling code is:
let result = mongo.isValidUsername(req.body.username).then((data) => {
return data;
});
if ((await result) == false) {
res.send("Sorry, that username is unavailable");
} else {
mongo
.addUser(
req.body.username,
req.body.password,
req.body.firstName,
req.body.lastName,
req.body.email,
req.body.phoneNumber
)
.then(() => {
let profileData = mongo.getProfileData(req.body.username);
profileData
.then((data) => {
res.render("accountDisplay", {
results: data,
trans: [9.99],
});
})
.catch((err) => {
console.log(err);
});
});
}
I call a query twice - Once in isValidUsername() at the beginning (where I have not used .clone) and then again in getProfileData( where I HAVE used .clone).
I keep getting this error. Any idea what could be causing it?
Here is the code for isValidUsername() and getProfileData(), just in case...
async function isValidUsername(usernameToQuery) {
//connect to mongoose database
mongoose.connect("mongodb://localhost:27017/bankDB");
try {
let isValid = UserModel.findOne({ username: usernameToQuery }).then(
(data) => {
if (data == null) {
return true;
} else {
return false;
}
}
);
return await isValid;
} catch (err) {
return err;
}
}
async function getProfileData(usernameToQuery) {
mongoose.connect("mongodb://localhost:27017/bankDB");
let profileData = UserModel.findOne({ username: usernameToQuery }).clone();
console.log(await profileData);
let profileArray = await profileData.then((data) => {
return [
data._doc.firstName,
data._doc.lastName,
data._doc.email,
data._doc.phoneNumber,
];
});
return await profileArray;
}
The Code in Users.js gets an error in the snippet at: qrcode.toDataURL(secret.otpauth_url, (err, data_url) => {.
I've tried adding return statement to make sure I'm not sending the response multiple times. I can see that the data_url when converted to image online shows me a QR code but I'm unable to see that when I'm using Postman.
router.post(
"/",
[
check("name", "Name is required")
.not().isEmpty(),
check("email", "Please include a valid email").isEmail(),
check(
"password",
"Please enter a password with 6 or more characters"
).isLength({ min: 6 })
],
async (req, res) => {
console.log("hi");
console.log(JSON.stringify(req.body));
const errors = validationResult(req);
if (!errors.isEmpty()) {
// return res.status(400).json({ errors: errors.array() });
}
const {
name,
email,
password,
type_of_user,
question1,
answer1,
question2,
answer2
} = req.body;
try {
let user = await User.findOne({ email }); // await User.findOne({ email });
user = new User({
name,
email,
avatar,
password,
type_of_user,
question1,
answer1,
question2,
answer2
});
const salt = await bcrypt.genSalt(10); //await
user.password = await bcrypt.hash(password, salt); // await
user
.save()
.then(result => {
// MFAOptions & secret will generate a secret
const MFAOptions = {
issuer: "xyz",
user: req.body.email,
length: 64
};
const secret = speakEasy.generateSecret(MFAOptions);
const token = jwt.sign(
{
name: user.name,
email: user.email,
twofactor: false
},
config.get("jwtSecret"), // chnaged from process env jwt
{
expiresIn: "1h"
}
);
// update the user that is just created:
user
.update(
{ email: req.body.email },
{
$set: { twoFASecret: secret.base32 }
}
)
.exec()
.then(result => {
console.log(result);
qrcode.toDataURL(secret.otpauth_url, (err, data_url) => {
console.log(data_url);
res.status(200).json({
img: data_url,
token: token
});
});
return;
})
//if anything wrong, throws an error
.catch(err => {
console.log(err);
// res.status(500).json({ error: err });
});
})
// originaly this will end here, but now it should redirect to twoFA route,
// if something wrong, shows an error
.catch(err => {
console.log(err);
// res.status(500).json({ error: err });
});
// user with an id, primise which returns an id
const payload = {
user: {
id: user.id
}
};
jwt.sign(
payload,
config.get("jwtSecret"),
{ expiresIn: 3600 },
(err, token) => {
if (err) throw err;
res.json({ token });
}
);
// } //else end
} catch (err) {
console.error(err.message);
res.status(500).send("Server error");
}
}
);
module.exports = router;
I think your problem with executing this line qrcode.toDataURL(secret.otpauth_url, (err, data_url) => {
this calling has callback which means that you will continue in executing the rest of the code and send a response using res.json then after qrcode finish it executes will enter the callback and send another response which is not allowed.
you have multi execution for res.json you need to remove one of them and refactor your code
I tried to refactor your code :
const validation = [check('name', 'Name is required').not().isEmpty(),
check('email', 'Please include a valid email').isEmail(),
check('password', 'Please enter a password with 6 or more characters').isLength({ min: 6 })]
const toDataURL = (otpauth_url) => new Promise((resolve, reject) => {
qrcode.toDataURL(secret.otpauth_url, (err, data_url) => {
if(err)reject(err)
resolve(data_url)
res.status(200).json({
img: data_url,
token,
})
})
});
const signJwt = (payload)=>new Promise((resolve,reject)=>{
return jwt.sign(
payload,
config.get('jwtSecret'),
{ expiresIn: 3600 },
(err, token) => {
if (err) reject(err)
resolve(token)
}
)
})
const postRequest = async (req, res) => {
const errors = validationResult(req)
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() })
}
const { name, email, password, type_of_user, question1, answer1, question2, answer2, } = req.body
try {
let user = await User.findOne({ email })
user = new User({
name,
email,
avatar,
password,
type_of_user,
question1,
answer1,
question2,
answer2,
})
const salt = await bcrypt.genSalt(10) // await
user.password = await bcrypt.hash(password, salt) // await
await user.save()
// MFAOptions & secret will generate a secret
const MFAOptions = {
issuer: 'xyz', user: req.body.email, length: 64,
}
const secret = speakEasy.generateSecret(MFAOptions)
const token = jwt.sign(
{
name: user.name,
email: user.email,
twofactor: false,
},
config.get('jwtSecret'), { expiresIn: '1h', })
// update the user that is just created:
await user.update({ email: req.body.email },
{ $set: { twoFASecret: secret.base32 }, }).exec()
const data_url= await toDataURL(secret.otpauth_url)
if(data_url) return res.status(200).json({
img: data_url,
token,
})
const payload = {
user: {
id: user.id,
},
}
const token= await signJwt(payload)
return res.json({token})
} catch (err) {
console.error(err.message)
return res.status(500).send('Server error')
}
}
router.post('/', validation, postRequest)
module.exports = router
My function generates a random number and checks if it already exists in the database. The problem is that I use this function when registering a new user and I need to add a promise here so that this function does not return null.
Could someone show me how I can write it, so that I can be sure that return getAccountBill() will be done first.
function getAccountBill() {
const accountBill = `2222${Math.floor(
Math.random() * 90000000000000000000,
) + 10000000000000000000}`;
Bill.findOne({
where: {
account_bill: accountBill,
},
})
.then(isAccountBill => {
if (isAccountBill) {
getAccountBill();
}
console.log('accountBill', accountBill);
return accountBill;
})
.catch(err => {
/* just ignore */
});
}
My register controller:
// Register Action
exports.register = (req, res) => {
function getAvailableFunds() {
const availableFunds = 0;
return availableFunds;
}
function getAccountBill() {
const accountBill = `2222${Math.floor(
Math.random() * 90000000000000000000,
) + 10000000000000000000}`;
Bill.findOne({
where: {
account_bill: accountBill,
},
})
.then(isAccountBill => {
if (isAccountBill) {
getAccountBill();
}
console.log('accountBill', accountBill);
return accountBill;
})
.catch(err => {
/* just ignore */
});
}
function getAccountBalanceHistory() {
const accountBalanceHistory = '0,0';
return accountBalanceHistory;
}
function getTodayDate() {
const today = new Date();
return today;
}
User.findOne({
where: { login: req.body.login },
}).then(isUser => {
if (!isUser) {
bcrypt.hash(req.body.password, 10, (err, hash) => {
req.body.password = hash;
User.create({
login: req.body.login,
password: req.body.password,
name: req.body.name,
surname: req.body.surname,
email: req.body.email,
date_registration: getTodayDate(),
})
.then(user =>
Bill.create({
id_owner: user.id,
account_bill: getAccountBill(), // <- this is null
available_funds: getAvailableFunds(),
})
.then(bill => {
Additional.create({
id_owner: user.id,
account_balance_history: getAccountBalanceHistory(),
})
.then(() => {
res.status(200).json({ register: true });
})
.catch(err => {
res.status(400).json({ error: err });
});
})
.catch(err => {
res.status(400).json({ error: err });
}),
)
.catch(err => {
res.status(400).json({ error: err });
});
});
} else {
res.status(400).json({ error: 'User already exists.' });
}
});
};
To return promise you need to add return it as it would be normal value.
So it should be:
...
const accountBill = `2222${Math.floor(
Math.random() * 90000000000000000000,
) + 10000000000000000000}`;
return Bill.findOne({
where: {
...