Hi all
recently I am learning about the auth.
I tried setting the accessToken and refreshToken for the auth.
Initially decided to use fetch for it. It did not work in my case. Exhausted all the combination of credentials.
credentials:true,
credentials:include,
etc.
I was really pissed and sad for like 3-4 days.
Finally decided to switch to axios also made some adjustment to my backend and it started working.
Let me show you my code.
My axios request
const loginCall = () => {
// You may get these data from the relevant selector
let data= {
email: "dummy@mail.com",
password: "123",
}
const options = {
method: "POST",
headers: {
"Content-Type": "application/json",
},
withCredentials: true, // Ensure Axios includes cookies in the request
};
axios("http://localhost:8002/auth/login", data,options)
.then((response) => {
// Check if response status is in the 200 range
if (response.status >= 200 && response.status < 300) {
console.log("response is: ", response);
// Print all headers from response
console.log("headers are: ");
console.log(response.headers);
let responseData = response.data;
console.log(responseData);
let accessToken = responseData.accessToken;
console.log("access token is: ", accessToken);
// Set the accessToken cookie correctly
document.cookie = `accessToken=${accessToken}; path=/; SameSite=Strict; expires=${new Date(
Date.now() + 1000 * 60 * 5
).toUTCString()}`;
console.log("cookie is: ", document.cookie);
// The refreshToken HttpOnly cookie will be automatically stored by the browser
} else {
throw new Error("Network response was not ok");
}
})
.catch((err) => console.error(err));
};
Put this in your index.js/server.js
const express = require("express");
const app = express();
let cors = require("cors");
app.use(
cors({
origin: "localhost",
credentials: true,
})
);
To add multiple domains do this.
origin: ["http://localhost:3000", "http://example.com", "https://subdomain.example.com"]
now the below code in your
let express = require("express");
let bcrypt = require("bcrypt");
let jwt = require("jsonwebtoken");
let User = require("../models/user-model");
// Login Route with JWT
router.post("/login", async (req, res) => {
try {
const { email, password } = req.body;
// Check if user exists
const user = await User.findOne({ email });
if (!user) {
return res
.status(401)
.json({ message: "Authentication failed, email not found" });
}
// Check password
// @ts-ignore
const isMatch = await bcrypt.compare(password, user.password);
if (!isMatch) {
return res
.status(401)
.json({ message: "Authentication failed, wrong password" });
}
// Generate JWT token with expiration time of 1 month (in seconds)
// jwt.sign({ id user._id, exp Math.floor(Date.now() / 1000) + (60 * 60 * 24 * 30) }, SECRET);
//@ts-ignore
const { accessToken, refreshToken } = signToken(user);
// httpOnly token way for the deployment
// res.cookie("accessToken", accessToken, {
// httpOnly: false,
// sameSite: "none",
// });
const expires = new Date(Date.now() + 60 * 60 * 1000);
res.cookie("refreshToken", refreshToken, {
httpOnly: true,
expires: expires,
sameSite: "none",
});
res
.status(201)
.json({ message: "User created successfully", accessToken: accessToken });
} catch (error) {
console.log(error);
res.status(500).json({ message: "Server Error" });
}
});
Pardon me for a lot of commented code.
I have removed some yet some remains.
So for me this code worked in the firefox browser
const expires = new Date(Date.now() + 60 * 60 * 1000);
res.cookie("refreshToken", refreshToken, {
httpOnly: true,
expires: expires,
sameSite: "none",
});
res.send("refresh Token sent")
For my friend using Brave browser the below code worked.
const expires = new Date(Date.now() + 60 * 60 * 1000);
return res
.status(200)
.cookie("refreshToken", refreshToken, {
path: '/',
httpOnly: true,
secure: true,
expires: expires,
sameSite: "none",
domain: "localhost",
})
.json({
accessToken: accessToken,
id: user._id,
role: user.role,
firstname: user.firstname,
lastname: user.lastname,
email: user.email,
message: "User logged In Successfully",
});
Now let me explain you these settings
First of all you must add the origin and credentials. Its a must. Else your tokens won't be sent or set.
const expires = new Date(Date.now() + 60 * 60 * 1000);
res.cookie("refreshToken", refreshToken, {
path: '/',
httpOnly: true,
secure: true,
expires: expires,
sameSite: "none",
domain: "localhost",
}
Now these cookie settings.
path: /
This means the cookie is valid across all the path.
If you want this to be valid to certain parts only say
/protected
httpOnly:true
This option is very crucial here. It tells the browser don't allow any JS touch it. Which means its totally secure. This is protected from the Cross Origin Attack
secure:true
This option means that browser will only send this cookie over https connection not http.
My reccomendation is to make a variable named cookieOptions or refreshTokenOptions.
Try setting the secure:true
in the production environment.
As you will go nuts when your backend on local system does not
get the cookies. As almost all of us have our local working on http.
let cookieOptions = {
httpOnly: true,
sameSite: "none",
}
if(process.env.NODE_ENVIRONMENT=="production")
cookieOptions.secure= true;
const expires = new Date(Date.now() + 60 * 60 * 1000);
expires: expires,
This sets the expiry time for the cookie to 1 hour.
You can set the time as per your choice.
My personal reccomendations are 3hour to a day.
Not more than that.
Also the time has to be given in miliseconds.
So to set 1 hour = 60mins X 60sec X 1000 milisec
sameSite: "none"
is used to send the cookies cross-site requests,
which basically means your backend and client can be on different ports and even different addresses.
domain: "localhost"
means its valid for only for localhost domain.
You may want to have multiple domains do it like this.
domain: ["localhost", "example.com", ".subdomain.example.com"]```