Every time React sends request to Express, a new session is generated - node.js

I use React as a client to send request to Express with proxy and express-session set up. But every time React makes a request to Express server, a new session is created. So I checked Express alone by manually accessing to the same api url and it keep using the same session each time I refresh the page.
Project structure:
project-folder
- client // React client with proxy set up
+ src
+ package.json
+ ...
- server.js
- package.json
Inside server.js:
const session = require('express-session');
let sessionConf = {
name: 'aoid',
secret: 'stackoverflow',
resave: true,
saveUninitialized: true,
rolling: true,
cookie: {
httpOnly: false,
secure: false,
maxAge: 2000000
}
};
app.use(session(sessionConf));
app.get('/api/prod', (req, res, next) => {
let sessionId = req.sessionID; // is generated each time React client send request, works fine with api alone!
console.log(sessionId);
if (!sessionId) return res.status(401).send('Unauthorized Error');
res.status(200).send({ data });
});
Here is how React client send its request to Express:
let loadItems = async () => {
const response = await fetch('/api/prod');
const body = await response.json();
if (response.status !== 200) throw Error(body.message);
return body;
}
I think the problem comes from the misconfiguration between React and Express. Did anyone have this problem before?

fetch does not send cookie by default, you need to set it explicitly:
fetch(url, {
method: 'GET',
credentials: 'include',
// ...
})

Related

React - httpOnly cookies not getting set from the express server

Vite + React: http://localhost:5173
Express: http://localhost:3000
Here is the code in the express server's login route:
const mycookie = cookie.serialize("jwt", refreshToken, {
httpOnly: true, // Set the HTTP-only flag
secure: true, // Set the secure flag
sameSite: "none",
path: "/", // Set the path of the cookie to '/'
maxAge: 3600, // Set the maximum age of the cookie to 1 hour
});
// Set the cookie in the response headers
res.setHeader("Set-Cookie", mycookie);
res.json({
accessToken,
refreshToken,
});
Here is my cors config:
const allowedOrigins = require("./allowedOrigins");
const corsOptions = {
crendials: true,
origin: function (origin, callback) {
console.log(allowedOrigins.indexOf(origin));
if (allowedOrigins.indexOf(origin) !== -1) {
callback(null, true);
} else {
callback(new Error("Not allowed by CORS"));
}
},
};
Here is my handleSubmit function in the frontend (gets called when the user clicks submit):
const handleSubmit = () => {
console.log("Login");
// axios.defaults.withCredentials = true;
axios.post("http://localhost:3000/api/v1/auth/login", { email, password });
};
Whenever the request is made to the server. The response header does contain the set-Header to set the jwt token but I am not able to see it in my applications tab under cookies in devtools. A pre-flight request also comes in which probably clears the cookie.
My networks tab:
The xhr request:
The OPTIONS request:
The Applications tab:
However, when I disable CORS in the browser, the cookie is getting set.
Networks tab (NOTICE: No PreFlight request)
Applications tab:
I tried working out different answers from stackoverflow answers like this, this, this, and many more along with a reddit post on a similar issue but nothing has worked.
P.S. : I already tried using credentials: true

MERN project: Cann't find the cookies on client side in production environment

Background:
I use express-session to do authetication in my MERN project.
After I logged in, a session will be created and sessionId will be send to client side by cookie. So when I am trying to fetch data, server will verify my sessionId.
I run my project in dev-env, and everything goes well. I can get the cookies in chrome devtools.
BUT after I deployed my project on Vercel(Server and Client depolyed separately). When I login in my web, there are no cookies in devtools.Verification failed, no sessionId returned.
Below are my codes:
server side:
//setup express-session
app.use(
session({
secret: process.env.SESSION_SECRET,
store: store,
resave: false,
saveUninitialized: false,
cookie: {
path: "/",
httpOnly: true,
secure: process.env.NODE_ENV === "PRODUCTION" ? true : false,
maxAge: 1000 * 60 * 60 * 24 * 7, // 1 week
},
name: "rd_sid",
})
);
//setup cors
const corsOptions = {
origin: process.env.CORS_ORIGIN.split(",").map(
(origin) => new RegExp(origin)
),
allowedHeaders: "Origin, X-Requested-With, Content-Type, Accept",
credentials: true,
};
app.use(cors(corsOptions));
client side:
export default function ajax(url, data = {}, method = "GET") {
return new Promise((resolve, isRejected) => {
let promise;
// 1.1 Execute ajax request
if (method === "GET") {
promise = axios.get(url, {
params: data,
withCredentials: true,
});
} else if (method === "POST") {
promise = axios.post(url, data, { withCredentials: true });
}
promise
.then((response) => {
// 1.2 If success, call resolve(value)
resolve(response.data);
})
.catch((error) => {
// 1.3 If fail, do not call reject(reason) but alert error message.
message.error("request error:" + error.message);
});
});
}
I set withCredentials to ture, and also set the header for cors. So it will solve the cross domain problem.
I try to switch secure , httpOnly, and change the cookie's name. But they seem not work.

Session Lost using nodejs express, cors, express-session

Working on a backend using nodejs, express, express-session, cors & cookie-parser to communicate with a react app that use axios to send http request, and using mariadb for the database.
Using both of them on localhost (3000 front end, 3001 backend) work fine, session is correctly saved and can be retrieved / used (I just pass the user data).
When deploying the backend on either local network or an EC2 instance from aws, the req.session return only the parameters set on "cookies" in the app.use(session({}) when called after being set on the login.
app.js:
const express = require('express');
const cors = require('cors');
const session = require('express-session');
const pool = require('./config/database');
const cookieParser = require('cookie-parser');
const app = express();
app.use(express.json());
app.use(cors(
{
credentials: true,
origin: true,
}
));
app.set('trust proxy', 1)
app.use(cookieParser());
app.use(session({
secret: 'cat on keyboard',
saveUninitialized: false,
resave: false,
cookie: { httpOnly: false, maxAge: 1000 * 60 * 60 * 24 }
}));
The req.session set
getAccountIdByEmail: async (req, res) => {
// connect & retrieve user from credentials //
req.session.logged_user = user[0][0];
return res.status(200).json({ success: user[0][0] })
};
The axios call from react app:
const fetchData = () => {
if (adress.charAt(0) == '/') {
adress = endpoint + adress;
}
axios({
method: method,
url: adress,
data: content,
withCredentials: true
})
.then((res) => {
setResponse(res.data);
})
.catch((err) => {
setError(err);
})
.finally(() => {
setloading(false);
});
};
At first i thougt it came from Nginx on Aws EC2, but it does the same calling directly on port 3001 of the instance, then i had the same issue on a local network.
I've tried also to use a store (express-session-mariadb-store or express-mysql-session), without success.
I think it might be tied to cors or headers, but couldn't pinpoint what doesn't work.
I noticed on express-session-npm
there is a disclaimer saying it is only for development and will have memory leaks if deployed in production

res.cookie ist not saved in the browser

I am currently trying to set up my react application and connect it to my backend. I am using JWT to create web tokens, which I then save via cookieparser.
Server is running on: http://localhost:3000/
Client is running on: http://127.0.0.1:8080/client/public/
Now, I am trying to make a post request from the client (react) and try to store the token (cookie) in the browser but it is not working. It constantly saves the cookie onto the localhost:3000 domain and not on the domain where the client is running. Down below the relevant code for backend and frontend:
**// This is the cors setup for Express**
app.use(cors({ origin: 'http://127.0.0.1:8080', credentials: true }))
**// This is the request router API for creating the user and the cookie (and sending it to the browser)**
router.post('/api/users', async (req, res) => {
const user = new User(req.body)
try {
await user.save()
const token = await user.generateAuthToken()
res.cookie('auth_token', token, { httpOnly: false, sameSite: 'none' })
res.set('Content-Type', 'application/json')
res.send(token)
} catch (e) {
res.status(400).send('Did not work')
}
})
**// This is the request from the client with Axios:**
axios.post('http://localhost:3000/api/users', {
name: this.state.userName,
email: this.state.userEmail,
password: this.state.userPassword,
}, {
withCredentials: true,
credentials: 'include'
})
.then( (response) => {
const data = response
console.log(data)
})
.catch( () => {
console.log('Cant access backend')
})
this.setState({
userName: '',
userEmail: '',
userPassword: ''
})
}
Images:
Setting up cors
this is the api router:
API Router for creating user and sending it to DB
this is the client (frontend) request
Post request to register user with axios
Thank you very much in advance for your help :)
You're storing the cookie in the server. For you to store in the client. Return the cookie and store it using the static js file in the client.
//Returned cookie
document.cookie = "auth_token=token returned by server;

Node JS, express-session, remove cookie from client browser

I work with app, that already has its own infrastructure. The task is to prevent user login in several browser. Our application has single app architecture, so ideally user should work only in one browser tab. And I have a problem. I can’t remove cookie from client.
I. Briefly.
App settings:
Server: NodeJS
Port: 8083
Client: VueJS
Port: 8088
I use module express-session to initialize session mechanism on server side and send cookies to client. Client hasn’t set cookies.
II. Details:
Server’s root file is index.js
I do the following in it:
Plug in express module:
const express = require('express')
Plug in cors module:
const cors = require('cors')
Add cors settings:
app.use(cors({
origin: 'http://localhost:8088',
credentials: true
}))
Then I initialize session in user.js file and receive client’s connects:
Plug in express-session module:
const session = require('express-session')
Plug in routing by express.Router():
const router = express.Router()
Add session settings:
const EIGHT_HOURS = 1000 * 60 * 60 * 8
const {
SESS_NAME = 'sid',
SESS_LIFETIME = EIGHT_HOURS,
SESS_SECRET = 'test',
NODE_ENV = 'development'
} = process.env
const IN_PROD = NODE_ENV === 'production'
Initialize session:
router.use(session({
name: SESS_NAME,
resave: false,
saveUninitialized: false,
secret: SESS_SECRET,
cookie: {
maxAge: SESS_LIFETIME,
sameSite: false,
// Must have HTTPS to work 'secret:true'
secure: IN_PROD
}
}))
Receive client queries by router.post()
So what I did:
I use req.session.destroy to remove session data and expect the browser logout user from certain browser and cookies clear.
req.session.destroy(err => {
if (err) {
return res.send({ error: 'Logout error' })
}
res.clearCookie(SESS_NAME, {path: '/'})
return res.send({ 'clearSession': 'success' })
})
Unfortunately nothing magic happens
I read different topics. For example, here (GitHub) offer the conclusion: use explicit cookie’s path indication in res.clearCookie method as shown above.
That didn’t work.
Wrote this setting {path: '/'} in cookies settings. Didn’t work too.
router.use(session({
name: SESS_NAME,
resave: false,
saveUninitialized: false,
secret: SESS_SECRET,
cookie: {
path: '/',
maxAge: SESS_LIFETIME,
sameSite: false,
// Must have HTTPS to work 'secret:true'
secure: IN_PROD
}
}))
And as wrote in express-session documentation (NPM:express-session) this path is the default path for cookie storage.
Add req.session = null in req.session.destroy:
req.session.destroy(err => {
            if (err) {
                return res.send({ error: 'Logout error' })
            }
            req.session = null
            res.clearCookie(SESS_NAME, {path: '/'})
            return res.send({ 'clearSession': 'success' })
        })
That didn’t work
delete req.session doesn’t work too.
So, how can I resolve this problem? What should I do?
adding .send('cleared cookie') made my browser clear its cache of the named cookie.
const logOutRequest = (req, res) => {
req.session.destroy((err) => {
res.clearCookie("notcookie").send('cleared cookie');
});
};
Have you tried removing the exact cookie by setting it to null that is lets say that you are dealing with a cookie named Views you could remove the cookie using req.session.Views = null
Instead of doing this
req.session.destroy(err => {
if (err) {
return res.send({ error: 'Logout error' })
}
req.session = null
res.clearCookie(SESS_NAME, {path: '/'})
return res.send({ 'clearSession': 'success' })
})
you could do the name of your session cookie and set that to null that is
req.session.sid= null
This removes the cookie from client browser
req.session.destroy(err => {
res.clearCookie("session-cookie-name", { path: "/" });
});
The most important key is setting 'domain' in the clearCookie method to solve your issue. Expressjs will return the following in the http response header. However, it seems that on the browser or some browsers that I tested, it doesn't know which cookie belonging to which domain to clear, hence, the cookie remains there. You do not need to include the path when calling clearCookie
Set-Cookie: mycookie=; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT
You have to set domain like below:
req.session.destroy(err => {
res.clearCookie("session-cookie-name", { domain: 'your-domain' });
});
Then response header will become
Set-Cookie: mycookie=; Domain=your-domain; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT
The browser will clear the cookie nicely!
this works for me
#Post('signout')
async signout(#Req() req: Request, #Res({ passthrough: true }) res: Response) {
const user = req.user
if (!user) return {}
await new Promise<void>((resolve, reject) => {
req.session.destroy((err) => {
if (err) {
reject(err)
} else {
res.clearCookie('ACCESS_TOKEN', {
domain: '.xxx.com'
})
res.clearCookie('REFRESH_TOKEN', {
domain: '.xxx.com'
})
res.clearCookie('connect.sid', {
domain: '.xxx.com'
})
resolve()
}
})
})
return {}
}

Resources