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",
});
};
Related
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);
};
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()
}
}
})
}
})
}
Scenario : When I create/register user1 ,the verification mail is sent to that email id and he(user1) is being verified successfully and I am able to change password for user1.
After creating user1 , I am creating/registering user2 ,where the verification email is sent to the account .After clicking the link , it's becomes INVALID
Overall , I am only able to create one user
Languages used : MERN stack
Backend => route.js :
const express = require("express");
const router = express.Router();
const User = require("../models/userModel");
const Doctor = require("../models/doctorModel");
const bcrypt = require("bcryptjs");
const jwt = require("jsonwebtoken");
const authMiddleware = require("../middlewares/authMiddleware");
const sendEmail = require("../utils/sendMail");
const Token = require("../models/tokenModel");
const Appointment = require("../models/appointmentModel");
const moment = require("moment");
router.post("/register", async (req, res) => {
try {
const userExists = await User.findOne({ email: req.body.email });
if (userExists) {
return res
.status(200)
.send({ message: "User already exists", success: false });
}
const password = req.body.password;
const salt = await bcrypt.genSalt(10);
const hashedPassword = await bcrypt.hash(password, salt);
req.body.password = hashedPassword;
const newuser = new User(req.body);
const result = await newuser.save();
await sendEmail(result, "verifyemail");
res
.status(200)
.send({ message: "User created successfully", success: true });
} catch (error) {
console.log(error);
res
.status(500)
.send({ message: "Error creating user", success: false, error });
}
});
router.post("/login", async (req, res) => {
try {
const result = await User.findOne({ data: req.body.userId });
console.log(result);
const user = await User.findOne({ email: req.body.email });
if (!user) {
return res
.status(200)
.send({ message: "User does not exist", success: false });
}
if (user.isVerified === false) {
return res
.status(200)
.send({ message: "User not Verified", success: false });
}
const isMatch = await bcrypt.compare(req.body.password, user.password);
if (!isMatch) {
return res
.status(200)
.send({ message: "Password is incorrect", success: false });
} else {
const dataToBeSentToFrontend = {
id: user._id,
email: user.email,
name: user.name,
};
const token = jwt.sign(dataToBeSentToFrontend, process.env.JWT_SECRET, {
expiresIn: "1d",
});
res
.status(200)
.send({ message: "Login successful", success: true, data: token });
}
} catch (error) {
console.log(error);
res
.status(500)
.send({ message: "Error logging in", success: false, error });
}
});
router.post("/get-user-info-by-id", authMiddleware, async (req, res) => {
try {
const user = await User.findOne({ _id: req.body.userId });
user.password = undefined;
if (!user) {
return res
.status(200)
.send({ message: "User does not exist", success: false });
} else {
res.status(200).send({
success: true,
data: user,
});
}
} catch (error) {
res
.status(500)
.send({ message: "Error getting user info", success: false, error });
}
});
router.post("/send-password-reset-link", async (req, res) => {
try {
const result = await User.findOne({ email: req.body.email });
await sendEmail(result, "resetpassword");
res.send({
success: true,
message: "Password reset link sent to your email successfully",
});
} catch (error) {
res.status(500).send(error);
}
});
router.post("/resetpassword", async (req, res) => {
try {
const tokenData = await Token.findOne({ token: req.body.token });
if (tokenData) {
const password = req.body.password;
const salt = await bcrypt.genSalt(10);
const hashedPassword = await bcrypt.hash(password, salt);
await User.findOneAndUpdate({
_id: tokenData.userid,
password: hashedPassword,
});
await Token.findOneAndDelete({ token: req.body.token });
res.send({ success: true, message: "Password reset successfull" });
} else {
res.send({ success: false, message: "Invalid token" });
}
} catch (error) {
res.status(500).send(error);
}
});
router.post("/verifyemail", async (req, res) => {
try {
const tokenData = await Token.findOne({ token: req.body.token });
if (tokenData) {
await User.findOneAndUpdate({ _id: tokenData.userid, isVerified: true });
await Token.findOneAndDelete({ token: req.body.token });
res.send({ success: true, message: "Email Verified Successlly" });
} else {
res.send({ success: false, message: "Invalid token" });
}
} catch (error) {
res.status(500).send(error);
}
});
Backend => sendEmail.js :
const nodemailer = require("nodemailer");
const bcrypt = require("bcrypt");
const Token = require("../models/tokenModel");
module.exports = async (user, mailType) => {
try {
const transporter = nodemailer.createTransport({
service: "gmail",
host: "smtp.gmail.com",
port: 587,
secure: true,
auth: {
user: "sh***********th#gmail.com",
pass: "e**************l",
},
});
const encryptedToken = bcrypt
.hashSync(user._id.toString(), 10)
.replaceAll("/", "");
const token = new Token({
userid: user._id,
token: encryptedToken,
});
await token.save();
let mailOptions, emailContent;
if (mailType === "verifyemail") {
emailContent = `<div><h1>Please click on the below link to verify your email address</h1> ${encryptedToken} </div>`;
mailOptions = {
from: "sh************th#gmail.com",
to: user.email,
subject: "Verify Email For MERN Auth",
html: emailContent,
};
} else {
emailContent = `<div><h1>Please click on the below link to reset your password</h1> ${encryptedToken} </div>`;
mailOptions = {
from: "shanshangeeth#gmail.com",
to: user.email,
subject: "Reset Password",
html: emailContent,
};
}
await transporter.sendMail(mailOptions);
} catch (error) {
console.log(error);
}
};
// auth: {
// user: "shanshangeeth#gmail.com",
// pass: "erwsvgtamrplzssl",
// },
Backend => authMiddleware.js :
const jwt = require("jsonwebtoken");
module.exports = async (req, res, next) => {
try {
const token = req.headers["authorization"].split(" ")[1];
jwt.verify(token, process.env.JWT_SECRET, (err, decoded) => {
if (err) {
return res.status(401).send({
message: "Auth failed",
success: false,
});
} else {
req.body.userId = decoded.id;
next();
}
});
} catch (error) {
return res.status(401).send({
message: "Auth failed",
success: false,
});
}
};
Backend => tokenmodel.js :
const mongoose = require("mongoose");
const tokenSchema = new mongoose.Schema(
{
userid: {
type: String,
required: true,
},
token: {
type: String,
required: true,
},
},
{ timestamps: true }
);
const tokenModel = mongoose.model("tokens", tokenSchema);
module.exports = tokenModel;
When I create/register user1 , the verification mail is sent to that email id and he(user1) is being verified successfully and I am able to change password for user1.
After creating user1 , I am creating/registering user2 ,where the verification email is sent to the account .After clicking the link , it's becomes INVALID
Overall , I am only able to create one user who's being verified
In the "verifyemail" route handler is you are trying to access the body of the req which is null, remember that when a user clicks on that URL in the email, a get request is send. The token will then exist in the req.params object, Not req.body.
Try the changes below.
router.get("/verifyemail/:token", async (req, res) => {
try {
const tokenData = await Token.findOne({ token: req.params.token });
if (tokenData) {
await User.findOneAndUpdate({ _id: tokenData.userid, isVerified: true });
await Token.findOneAndDelete({ token: req.params.token });
res.send({ success: true, message: "Email Verified Successlly" });
}
I read a lot of questions like this but no one is usefull for me.
I have a MERN app. I use Heroku to deploy. In my local environent everithing works but in heroku the login crash. I use Cookies. and try every posible configuration.
Please help.
This is my login server:
login: async (req, res) => {
try {
const { email, password } = req.body;
const user = await Users.findOne({ email })
//console.log(user)
if (!user) return res.status(400).json({ msg: "Usuario inexistente" })
const isMatch = await bcrypt.compare(password, user.password)
if (!isMatch) return res.status(400).json({ msg: "Clave erronea" })
//If login success, craete access Token and refresh
const accesstoken = createAccessToken({ id: user._id })
const refreshtoken = createRefreshToken({ id: user._id })
//console.log(refreshtoken)
res.cookie('refreshtoken', refreshtoken, {
sameSite: 'strict',
httpOnly: true,
path: '/user/refresh_token',
maxAge: 7 * 24 * 60 * 60 * 1000 //7days
})
res.json({ accesstoken })
} catch (err) {
return res.status(500).json({ msg: err.message })
}
},
Here server conf.
//MIDDELEWARES
app.use(express.json())
app.use(cookieParser())
//app.use(cors())
/*app.use(cors({
credentials: true,
origin: 'https://gabymanualidades.herokuapp.com/'
}))*/
app.use(cors({origin: 'https://*****.herokuapp.com', methods: ['POST', 'PUT', 'GET', 'OPTIONS', 'HEAD'], credentials: true, headers: 'Authorization, X-API-KEY, Origin, X-Requested-With, Content-Type, Accept, Access-Control-Allow-Request-Method' }))
app.use(fileUpload({
useTempFiles: true
}))
And client:
const loginSubmit = async e => {
e.preventDefault()
try {
//await axios.post('/user/login', { ...user })
const res=await axios.post('/user/login', { ...user }, {
headers: {
'Content-Type': 'application/json'
},
withCredentials: true,
baseURL: "*****.herokuapp.com"
})
console.log(res)
localStorage.setItem('firstLogin', true)
//window.location.href = "/products";
} catch (err) {
alert(err.response.data.msg)
}
}
Thak you very much!
Screenshot:
It is as if I logged in without saving the cookie since then I can't get it again here:
useEffect(() => {
const firstLogin = localStorage.getItem('firstLogin')
if (firstLogin) refreshToken()
}, [])
const refreshToken = async () => {
try {
console.log("aca problema")
const res = await axios.get('/user/refresh_token', {
headers: {
'Content-Type': 'application/json'
},
withCredentials: true,
baseURL: "https://young-wildwood-03509.herokuapp.com/"
})
setToken(res.data.accesstoken)
setTimeout(() => {
refreshToken()
}, 10 * 60 * 1000)
} catch (err) {
alert(err.response.data.msg)
}
}
Server:
refreshToken: (req, res) => {
try {
//console.log(req.cookies)
const rf_token = req.cookies.refreshtoken;
if (!rf_token) return res.status(401).json({ msg: "Plase Login or registeer" })
jwt.verify(rf_token, process.env.REFRESH_TOKEN_SECRET, (err, user) => {
if (err) return res.status(400).json({ msg: "Plase Login or registerr" })
const accesstoken = createAccessToken({ id: user.id })
//res.json({user, accesstoken})
console.log({ accesstoken })
res.json({ accesstoken })
})
//res.json({ rf_token })
} catch (err) {
return res.status(500).json({ msg: err.message })
}
},
I am trying to configure my JWT through http-only cookies
const signIn = async (req, res) => {
const { email } = req.body;
try {
const user = await User.findOne({ email });
const access_token = jwt.sign({ id: user._id }, jwtSecret, {
expiresIn: token_expiry_time,
});
const refresh_token = jwt.sign({ id: user._id }, jwtRefreshSecret, {
expiresIn: refresh_token_expiry_time,
});
res.cookie('access_token', access_token, { httpOnly: true });
res.cookie('refresh_token', refresh_token, { httpOnly: true });
return res.status(200).json({
status: true,
data: {
user: {
id: user._id,
},
},
});
} catch (err) {
Server.serverError(res, err);
}
};
but at the client-side it refuses to set the cookie in the browser, it returns the error "cannot set cookie because same-site is not 'None' ", After setting sameSite:'None' on the server side, it then gave the error "same-site set to 'None' needs to have a 'Secure' field", I then set secure:true on the backend but it doesn't work because I am not using https for development.
client-side code
const onSubmit = (cred) => {
setLoading(true);
restAPI
.post('auth/signin', cred)
.then(({ data }) => {
setLoading(false);
setError(false);
console.log(data.data);
})
.catch((err) => {
setLoading(false);
setError({
status: true,
message: err.response.data.message,
});
});
};
how can I solve this?