Express sessionID changes on every client's request - javascript

My problem:
When I go to server adress (so I'm using get method) it is working as I would want it to work, the sessionID doesn't change upon HTTP requests, but when I'm using client's fetch method to get to the server adress, the sessionID always changes and that is defect, what I don't want.
Any ideas why this is happening and how could I fix it?
Code of how my sessions are set up:
const session = require('express-session');
...
app.set("trust proxy", 1);
app.use(
session({
secret: process.env.SESSION_SECRET,
saveUninitialized: true,
resave: false,
cookie: {
secure: false,
sameSite: true,
},
})
);
...
app.get("/lobby/:id", (req, res) => {
console.log(req.sessionID);
req.session.test = 1;
});
Client's request
useEffect(() => {
fetch(getServerAdress() + "/lobby/" + code, {
method: "GET",
})
.then((response) => response.json())
.then((data) => setLoading(false))
.catch(() => setLoadingText("Failed to join the lobby"));
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

As Mat J. said, fetch does not send cookies for cross-origin by default, so I had to change it:
fetch(getServerAdress() + "/lobby/" + code, {
method: "GET",
credentials: "include",
}
Also I had to enable credentials and origin for CORS on my server:
const cors = require("cors");
app.use(cors({ credentials: true, origin: true }));

Related

Express JS set a cookie doesn't work when using res.json

I have a login route where I want to set a cookie after I verify the login credentials. The client and the server are on different ports.
const app = express();
app.use(
cors({
credentials: true,
origin: true,
})
);
app.use(cookieParser());
app.use('/login', (req, res) => {
res.cookie('secureCookie', JSON.stringify({ id: 1 }), {
secure: false,
httpOnly: true,
});
return res.json({ success: true });
});
app.use('/check', (req, res) => {
console.log(req.cookies);
return res.json({ id: 1 });
});
The issue is that I don't see the cookie in the devtools (applications tab) after the login request returns. Also, when trying to fetch the check endpoint using credentials: 'include' it doesn't send the cookie.
What I'm doing wrong?
Here are the requests:
fetch('http://localhost:4000/login');
fetch('http://localhost:4000/check', {
credentials: 'include',
});
According to Using Fetch article on mdn
Unless fetch() is called with the credentials option set to include, fetch():
won't send cookies in cross-origin requests
won't set any cookies sent back in cross-origin responses
credentials: include must be set for requests to make them save cookies
just delete return
res.json({ success: true });

Cookie is not included in request header / Server side cannot read req.cookies

I am learning and applying authentication for my blog website!
I am using express-session to handle logins. Cookie on the browser & server sessions works fine.
However, I am having trouble retrieving cookies on the server-side express app. I tried the following:
With cookie-parser, req.cookies & req.signedCookies both returns [Object: null prototype].
Setting CORS
req.cookie & req.header.cookie returns undefined
I can see a "Cookie" header from my connection in the browser network tab.
My code / settings are as follows:
function auth (req, res, next) {
// Problem: Cannot read browser cookie of HTTP requests.
console.log('Based on browser', req.cookie, req.cookies, req.signedCookies);
next();
}
router.get('/', auth, async (req, res) => { // ... }
Middlewares
app.use(cors({
origin: ['http://localhost:3000'],
credentials: true
}));
app.use(cookieParser()) // Also tried with secret option.
app.use(session({
secret: 'top-secret',
resave: true,
rolling: true,
saveUninitialized: false,
store: store, // this is working
cookie: {
maxAge: 1000 * 60 * 60 * 24 * 14,
httpOnly: true,
secure: process.env.NODE_ENV !== 'Development',
sameSite: process.env.NODE_ENV === 'Development' ? 'lax' : 'none'
}
}))
Thank you in advance :)
Edit 1: My fetch code:
If your using http only you should consider 2 things:
Step1 while request in client side:
you should send request like this:
const req = await fetch("http://localhost:7000/api/auth/login", {
method: "POST",
credentials: "include",
headers: {
"Content-Type": "application/json",
"Access-Control-Allow-Credentials": true,
},
body: JSON.stringify({
email: formData.get("email"),
password: formData.get("password"),
}),
});
const data = await req.json();
step 2 in express:
const allowedOrigins = ["http://localhost:8000"];
const corsOptions = {
origin: function (origin, callback) {
if (allowedOrigins.indexOf(origin) !== -1) {
callback(null, true);
} else {
var msg =
"The CORS policy for this site does not " +
"allow access from the specified Origin.";
callback(new Error(msg), false);
}
},
optionsSuccessStatus: 200,
credentials: true,
};
app.use(cors(corsOptions));
now you can get coockies in express by using req.cookies.nameOfCookiesWhichYouSendThroughCoockieParser
I'm using axios (React) + Express-js on Node-js
In order to get the cookie from the server:
Simply set withCredentials: true in the axios request, you can use this config example:
const config = {
headers: {
"Content-Type": "application/json",
},
withCredentials: true,
};
In order to get this cookie from the client:
You also need to set withCredentials: true in the axios request,
And you need to install cookie-parser library on the server:
npm i cookie-parser
Import this library:
const cookieParser = require("cookie-parser");
And use the cookieParser middleware:
app.use(cookieParser());
And finally, req.cookies should return the list of your cookies.

How to locally test set Cookies with the Same Origine Policy

I have a problem when testing my web app locally.
I set cookies in a request response (here is how) :
const token = createToken(user._id);
res.cookie("jwt", token, { httpOnly: true, maxAge: maxAge, });
res.status(201).json({ user: user._id });
return res;
But I encounter an error about Same domain Policy when executing the following request in the React Client:
axios.post(`${process.env.REACT_APP_API_URL}api/user/login`, {
email: email,
password: password,
},
{
withCredentials: true
})
.then((res) => {
if (res.data.errors) {
//Show errors
}
})
.catch((err) => {
console.log(err);
});
I tried to execute it with the parameter withCredentials : false
The request works but the cookie is not stored
But I have set-Cookie in the response of my request
And this is my CORS options :
const corsOptions = {
origin: process.env.CLIENT_URL,
credentials: true,
allowedHeaders: ["sessionId", "Content-Type"],
exposedHeaders: ["sessionId"],
methods: "GET,HEAD,PUT,PATCH,POST,DELETE",
preflightContinue: false
};
I remplace my cors options by :
const corsOptions = {origin: process.env.CLIENT_URL,credentials: true};
and add :
app.options('*', cors(corsOptions));
and now it's working

req.isAuthenticated() in express and passport-local returns false

I hope someone can shed some light on my situation. The req.isAuthenticated() always returns false, after being called in an app.router endpoint(running in port 3001), via fetch API. It appears, the connect.sid was not successfully passed on req arg when I do req.isAuthenticated()
On the other hand, my react dev server runs on port 3000
Here is my current setup.
Login Route, which authenticates username and password, and returns connect.sid via cookie value.
const express = require('express')
const router = express.Router()
const passport = require('passport')
...
router.post( '/authenticate', passport.authenticate('local'), ( req, res, next ) => {
res.status( 200 ).json({
'response': 'Welcome User',
'redirect' : '/dashboard'
})
})
...
module.exports = router
At this point, my Users Route should be able to access the protected route. which simply returns all users on the database.
const express = require('express')
const router = express.Router()
const SimpleCmsUsers = require('../models/Users.models.js')
const authPassportLocal = require('../passport/auth.PassportLocal.js')
...
router.get( '/', authPassportLocal ,( req, res, next ) => {
console.log( req.headers )
console.log( req.body )
SimpleCmsUsers
.find({})
.then(( users ) => {
return res.status( 200 ).json( users )
})
.catch(( error ) => {
return res.status( 403 ).json( error )
})
})
...
module.exports = router
My auth.PassportLocal.js which checks the value of req.isAuthenticated()
const authPassportLocal = ( req, res, next ) => {
console.log( req.headers ) // I do not see session has been passed on my request
console.log( req.body ) // empty
console.log('isAuthenticated', req.isAuthenticated() ) // log if isAuthenticated returns true.
if ( req.isAuthenticated() ) {
return next()
}
return res.redirect('/dashboard/login/index')
}
....
module.exports = authPassportLocal
Now, when I call /dashboard/users the via fetch API
fetch( '/dashboard/users', {
headers :{
'Content-Type' : 'application/x-www-form-urlencoded'
},
credentials: 'include',
})
.then(( response ) => response.json())
.then(( users ) => console.log( users ))
.catch(( error ) => console.log( error ))
this returns isAuthenticated false, I tried to view the headers received from, /dashboard/users, however, I do not see any cookies passed on my request.
Here is my index.js
const express = require('express')
const session = require('express-session')
const flash = require('express-flash')
const cors = require('cors')
const passport = require('passport')
const LocalStrategy = require('passport-local').Strategy
const Users = require('./routes/Users.routes.js')
const Login = require('./routes/Login.routes')
const SimpleCmsUsers = require('./models/Users.models.js')
const app = express()
app.use( express.json() )
app.use( express.urlencoded({ extended: true }) )
app.use( cors({
origin: ['http://localhost:3001', 'http://localhost:3000'],
credentials: true
}))
app.use( flash() )
app.use( session({
secret: 'EUE7J3lUE01xhmCGQt04S8PbsMpUE5JDcQj0fyS0cy73PQVDLM',
resave: true,
saveUninitialized: true
}))
app.use( passport.initialize() )
app.use( passport.session() )
passport.use( new LocalStrategy(
{
// passport-local option here ...
},
( username, password, done ) => {
try {
SimpleCmsUsers.find({ user_username : username, user_password : password }, function ( err, docs ) {
if ( !docs.length ) {
return done( null, false, { message: "User not found!" } )
}
return done( null, username )
})
}
catch( error ) {
return done( null, false, { message: error } )
}
}
))
passport.serializeUser(function( user, done ) {
done( null, user );
})
passport.deserializeUser(function( user, done ) {
done( null, user );
})
app.use('/dashboard/users', Users)
app.use('/dashboard/login', Login)
app.listen( PORT, () => console.log("Express JS is on port " + PORT) )
What bothers me most, these current setup works on Postman without this type of challenge.
In my fetch API request (http://localhost:3001/dashboard/users'):
url /
headers {
host: 'localhost:3001',
connection: 'keep-alive',
pragma: 'no-cache',
'cache-control': 'no-cache',
'sec-fetch-dest': 'empty',
'user-agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.87 Safari/537.36',
dnt: '1',
'content-type': 'application/x-www-form-urlencoded',
accept: '*/*',
origin: 'http://localhost:3000',
'sec-fetch-site': 'same-site',
'sec-fetch-mode': 'cors',
referer: 'http://localhost:3000/dashboard/users',
'accept-encoding': 'gzip, deflate, br',
'accept-language': 'en-US,en;q=0.9,fil;q=0.8',
cookie: 'connect.sid=s%3AJEG3MNSqtl33KqmHR2DhGlslnlkMKIPT.xsI%2F%2B82%2F1x8zTlq%2BkRN6aJVVbrauH8qv8jDhsrvNlbY'
}
body {}
user undefined
session Session {
cookie: { path: '/', _expires: null, originalMaxAge: null, httpOnly: true }
}
isAuthenticated false
In Postman (http://localhost:3001/dashboard/users'):
url /
headers {
'content-type': 'application/x-www-form-urlencoded',
'user-agent': 'PostmanRuntime/7.22.0',
accept: '*/*',
'cache-control': 'no-cache',
'postman-token': '443a064e-7909-43db-9783-79a6ba8bd4c5',
host: 'localhost:3001',
'accept-encoding': 'gzip, deflate, br',
cookie: 'connect.sid=s%3AsvbYi_oxm4yqXTTa7S-N-3qAT6BdW5-u.QYFAXzayArpV1%2BDbjnwJ3fMMjpLzkM%2Fr9kIUCUCYscY',
connection: 'keep-alive'
}
body {}
user username
session Session {
cookie: { path: '/', _expires: null, originalMaxAge: null, httpOnly: true },
passport: { user: 'username' }
}
isAuthenticated true
It just I can not see what went wrong, why fetch API cannot pass the cookie value from connect.sid
Any help or guide where to isolate this behavior better are highly appreciated.
TA
Update
npm run start // for react dev server running in port 3000
nodemon api/v1/index.js // for express api running in port 3001
Tried these threads below, however, I do not see any progress on my end:
passport's req.isAuthenticated always returning false, even when I hardcode done(null, true)
Basics of Passport Session (expressjs)-why do we need to serialize and deserialize?
Your code is correct.
Only question is that how cookies being set.
Since You're backend runs at :3001 cookies being set as for host: localhost:3001.
Read this issue: https://github.com/jaredhanson/passport/issues/403
solution is to make session middleware to set cookie by different host
app.use( session({
secret: 'EUE7J3lUE01xhmCGQt04S8PbsMpUE5JDcQj0fyS0cy73PQVDLM',
resave: true,
saveUninitialized: true,
cookie: {domain: 'localhost:3000'}
}))

nodejs - Set-Cookie present in response but missing in browser

I'm working with express.js and passport.js as my backened with axios and vue.js as my frontend.
I can see my set-cookie but the cookie is not present in my browser. Pictures linked below.
Response headers
Request headers
This cookie is used to authenticate my user using passport.js.
The following is the code for my express.js.
var app = express();
app.use(cors({
origin: "http://localhost:8080",
credentials: true
}));
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({
extended: false
}));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use(session({
secret: 'p#ssw0rd'
}));
app.use(passport.initialize());
app.use(passport.session());
The method below will be called through vue.js methods.
const url = "http://localhost:3000/";
let axiosConfig = {
withCredentials: true,
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': 'http://localhost:3000/',
'Access-Control-Allow-Methods': 'GET,PUT,POST,DELETE'
}
}
class user {
static login(username, password) {
return new Promise(async(resolve, reject) => {
try {
const res = await axios.post(
`${url}login`, {
username,
password
},
axiosConfig
);
resolve(res.data);
} catch (err) {
reject(err);
}
});
}
Backend is run at localhost:3000 and frontend is run at localhost:8080
Inspecting cookies.
No data presented for selected host
I found the answer to my question and the solution is really stupid, nothing code related.
Since I am working on expressjs and a friend works on vuejs, he added two of the same folder that contains the axios methods. I changed the part which is not referenced.
The code I posted in my question works.

Categories