res cookie doesnt update cookies in the browser - node.js

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()
}
}
})
}
})
}

Related

i got "Error: data and hash arguments required" on my code

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"});
}
}

Set Cookie not working (nextJs + express)

I'm trying to set the cookie from my express backend to my nextjs front end using the response.setHeader, but it's not working, I get my json response but no cookies are set, I used postman to make some tests and in postman it did set the cookies as it should be.
NextJs version is 13.0.2, express version is 4.18.2 and cookie version is 0.5.0
My express server:
export const loginUser = async (req: Request, res: Response) => {
const { email, password } = req.body;
//limpa os cookies
res.clearCookie("accessToken", { httpOnly: true });
if (email !== "" && password !== "") {
try {
const foundUser: any = await prisma.users.findFirst({
where: {
email,
password,
},
});
try {
const accessToken = jwt.sign(
{ id: foundUser.id, email },
"dspaojdspoadsaodksa",
{
expiresIn: "10s",
}
);
const refreshToken = jwt.sign(
{ id: foundUser.id, email },
"dsaoindsadmnsaosda",
{
expiresIn: "50s",
}
);
const savedRefresh = await prisma.refresh_token.upsert({
where: {
users_id: foundUser.id,
},
update: {
access_token: accessToken,
refresh_tk: refreshToken,
users_id: foundUser.id,
},
create: {
access_expires_in: "",
refresh_expires_in: "",
access_token: accessToken,
refresh_tk: refreshToken,
users_id: foundUser.id,
},
});
res.setHeader(
"Set-Cookie",
cookie.serialize("accessToken", accessToken, {
maxAge: 1000 * 60 * 15, //15 minutes
httpOnly: true, // The cookie only accessible by the web server
})
);
res.json({ accessToken, user: { email }, ok: true });
} catch (error) {
console.log("refresh cant be created");
res.send({ message: "refresh cant be created" });
}
} catch (error) {
res.send({ message: "user not found" });
}
} else {
res.json({ message: "token creation failed" });
}
};
My nextJs front-end:
const handleLogin = async (event: React.SyntheticEvent) => {
event.preventDefault();
const email = (event?.target as any).email.value;
const password = (event?.target as any).password.value;
if (email === "" || password === "") {
setError(true);
return;
}
const data = {
email,
password,
};
const resp = await fetch("http://localhost:5000/login", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(data),
});
const userData = await resp.json();
console.log(userData);
};

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",
});
};

Unable to set headers with cookie after sending json response

Building out some basic login functionality with JWT's and I am running into an error with setting the cookie... Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
From what I have seen it is because of the middleware going to next and not ending the previous... I just tried res.end(data) while assigning the constant data to the json response. Any advice?
router.post('/users/login', async (req, res) => {
try {
let userInput = {
email: req.body.email,
password: req.body.password
}
const userCheck = await User.findOne({ email: req.body.email })
if (!userCheck) {
res.json("No User with that email!")
} else {
const valid = await bcrypt.compare(req.body.password, userCheck.password)
if (!valid) {
res.json("Invalid password for " + userCheck.email)
} else {
try {
const refreshToken = jsonwebtoken.sign({ userId: userCheck._id }, process.env.REFRESH_TOKEN_SECRET, { expiresIn: '7d' })
const accessToken = jsonwebtoken.sign({ userId: userCheck._id, }, process.env.ACCESS_TOKEN_SECRET, { expiresIn: '15m' })
res.json("AccessToken: " + accessToken)
res.cookie("bearer", refreshToken, {
httpOnly: true
})
} catch (err) {
res.json("Error: " + err)
}
}
}
} catch (err) {
res.status(400).json("Error: " + err)
}
})
You just have to call res.cookie() BEFORE you send the response with res.json(). res.cookie() sets the cookie header for the response and that has to be in place before the response is sent. When you call res.json() all headers that have already been configured are sent with the response and you can no longer set any more headers because the response will have already been sent and you only get to send one response.
So, change this:
try {
const refreshToken = jsonwebtoken.sign({ userId: userCheck._id }, process.env.REFRESH_TOKEN_SECRET, { expiresIn: '7d' })
const accessToken = jsonwebtoken.sign({ userId: userCheck._id, }, process.env.ACCESS_TOKEN_SECRET, { expiresIn: '15m' })
res.json("AccessToken: " + accessToken)
res.cookie("bearer", refreshToken, {
httpOnly: true
})
} catch (err) {
res.json("Error: " + err)
}
to this:
try {
const refreshToken = jsonwebtoken.sign({ userId: userCheck._id }, process.env.REFRESH_TOKEN_SECRET, { expiresIn: '7d' })
const accessToken = jsonwebtoken.sign({ userId: userCheck._id, }, process.env.ACCESS_TOKEN_SECRET, { expiresIn: '15m' })
res.cookie("bearer", refreshToken, { // <== This goes before res.json()
httpOnly: true
})
res.json("AccessToken: " + accessToken)
} catch (err) {
res.json("Error: " + err)
}

errors handling on login with req.flash

I have an issue with not getting just flash message on my page, but getting json with an error instead of it:
However, if I click "back" in browser, I see the page as expected, with flash message on it
My request code:
const handleLogin = async (req, res) => {
const { errors, isValid } = validateLoginInput(req.body);
if (!isValid) {
return res.status(422).json(errors);
}
const { email, password } = req.body;
const user = await User.findOne({email});
if (!user) {
errors.email = AUTH_ERROR;
req.flash('loginMessage', AUTH_ERROR);
return res.status(404).json(errors);
}
const isMatch = user.validatePassword(password, user.password);
const { id, role } = user;
if (isMatch) {
const payload = {id, email, role};
jwt.sign(
payload,
config.JWTAuthKey,
{expiresIn: 3600},
(err, token) => {
res.cookie(tokenCookieName, token, { maxAge: 60 * 60 * 24 * 7 , httpOnly: false });
res.redirect('/');
}
);
} else {
errors.password = AUTH_ERROR;
req.flash('loginMessage', AUTH_ERROR);
return res.status(403).json(errors);
}
};
In addition, my passport config (I use jwt strategy)
const config = (passport) => {
passport.use(
new Strategy(opts, (jwt_payload, done) => {
User.findById(jwt_payload.id)
.then((user) => {
if (user) {
return done(null, user);
}
return done(null, false);
})
/*eslint no-console: ["error", { allow: ["warn", "error"] }] */
.catch(err => console.error(err));
}),
);
};
Any ideas would be highly appreciated, thank you un advance.
It turned out to be pretty easy. No need to send back status, just
return res.redirect('/')
will do the trick. One can redirect wherever is needed.

Resources