Express session is empty in Safari - node.js

I'm building a Node.js application with express. I use express-session for sessions. The data I store in a session is available in IE, Chrome and Firefox. But in Safari the session is empty all the time. So when I do console.log(req.session) it prints:
Session {
cookie: {
path: '/',
_expires: null,
originalMaxAge: null,
httpOnly: true,
secure: true
},
userHasCheckCorrect: true
}
This are my settings in server.js
app.use(session({
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: true,
cookie: {
secure: true,
httpOnly: true
}
}));
It's a server with SSL certificate so it's a HTTPS domain. With HTTP it seems to work fine in Safari. What am I doing wrong here? Is there a setting I'm missing? I've looked on many places but couldn't find the answer yet. And why does it work in all browsers expect for Safari?

With Safari it is required to pass in credentials: 'include' in request header, otherwise it doesn't send cookies.
You might also want to set a global response header after your set your session middleware by something like this:
app.use(function(req, res, next) {
res.set('credentials', 'include');
res.set('Access-Control-Allow-Credentials', true);
res.set('Access-Control-Allow-Origin', req.headers.origin);
res.set('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
res.set('Access-Control-Allow-Headers', 'X-Requested-With, X-HTTP-Method-Override, Content-Type, Accept');
next();
});

Related

Nodejs set Cookies not being sent to firebase frontend react app

I'm trying to set cookies on the frontend firebase from nodejs app on a different server.
I have allowed whitelisted the frontend URL from cors.
Also, set the headers.
appRouter.use(function (req, res, next) {
// Website you wish to allow to connect
res.setHeader('Access-Control-Allow-Origin','*');
// Request methods you wish to allow
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE');
// Request headers you wish to allow
res.setHeader('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
// Set to true if you need the website to include cookies in the requests sent
// to the API (e.g. in case you use sessions)
res.setHeader('Access-Control-Allow-Credentials', true);
res.cookie('cokkieName',138932843821341, { maxAge: 900000, httpOnly: false, sameSite: 'none', secure: true })
// Pass to next layer of middleware
next();
}
)
appRouter.use(cors(
{
credentials: true,
origin: [
'http://localhost:3000',
'https://mywebsitename.com',
'https://localhost:3000'
]
}));
// parse application/x-www-form-urlencoded
appRouter.use(bodyParser.urlencoded({ extended: false }))
appRouter.use(cookieParser("secret"));
appRouter.use(session({
cookie:{
httpOnly:false,
maxAge: 1*60*60*1000},
resave: false,
saveUninitialized: true,
secret: 'secret' ,
sameSite:false,
secure: true
}));
Through postman or even through running the backend(which is on a different server) URL, I do get the cookies. But on the firebase frontend, I don't get the cookies.
There was a cors error but I resolved it by adding the frontend URL to the origin whitelist.
The backend is communicating with the frontend. I say this because the backend response is logged on the browser and the data is visible. However, the cookies are not.
I also have google authentication which, after successfully closing the window of account selection, sets the various google auth cookies to the frontend.

Unsure as to why no cookie is being sent back to my localhost client when using express-session

Having issues with node express-session and basically just trying to understand how it all works with regards to cookies and my session store within my postgres database.
For starters, I'm not sure why I don't receive a session id cookie within my chrome browser where my react app is running on localhost:3000. If I call the route localhost:5000/login from postman, a cookie is received but when calling the same route from Chrome: localhost:5000/login and then check my cookies, nothing is created when using the fetch API.
The session is created fine within postgres.
Here is my middleware for session setup:
app.use(session({
store: new pgSession({
pool : pool, // Connection pool
schemaName: 'my_schema',
tableName : 'user_sessions'
}),
secret: randomString.generate({
length: 14,
charset: 'alphanumeric'
}),
resave: false,
saveUninitialized: false,
cookie: { maxAge: 1000 * 60 * 60 * 24,
httpOnly: true }
}))
app.use(function (req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
res.header('Access-Control-Allow-Methods', 'PUT, POST, GET, DELETE, OPTIONS');
next();
});
My other question is, within my react app, how can I use the session info within my postgres db to check that all requests to all routes are still coming from the same user on the client side?
try this , change your cors middleware this
app.use(cors({ origin: true, credentials: true }));
and add this when you make your request
withCredentials: true,
credentials: "include",
I had same problem I was recieving the cooking when making post from postman, but not from the browser when making request with axios, hopefully it works for you too as well

Having troubles sharing sessions between subdomains in express

I'm trying to share a session between multiple subdomains (under the same root domain), so user can login in dashboard.example.com, and when viewing x.example.com, he will be identified on backend.
I'm using express with express-session and passport.js and my backend server is sitting behind NGINX server. I went through all topics I could find here, but none of them helped me fix the issue.
Here is my express-session configuration:
{
store: redisStore,
secret: 'secret',
name: 'app.sid',
resave: false,
saveUninitialized: true,
proxy: true,
cookie: {
secure: true,
expires: Date.now() + parseInt(process.env.COOKIE_EXPIRATION_MS, 10),
maxAge: parseInt(process.env.COOKIE_EXPIRATION_MS, 10),
httpOnly: true,
domain: '.example.com'
}
}
I read on some threads here I should set response headers as well:
app.use((req, res, next) => {
res.header('Access-Control-Allow-Credentials', true)
res.header('Access-Control-Allow-Origin', '*')
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE')
res.header('Access-Control-Allow-Headers', 'X-Requested-With, X-HTTP-Method-Override, Content-Type, Accept')
next()
})
When logging into the app through dashboard.example.com, the session is created fine, and on browser I see the cookie session as expected (app.sid=123)
When going to x.example.com, the same session is being sent to the server (app.sid=123), but the session is not extracted from the redis store. Instead, it overrides '123' key in redis with an empty state, and now the state is also gone for dashboard.example.com
Any help / directions would be appreciated!
Thanks!

Redis session is refreshing on every new request from Angular HTTP GET, but NOT from Browser

I implemented a Redis session store with NodeJS (+ express). This is the code for the session:
app.use(cookieParser());
const redisClient = redis.createClient(config.redis);
app.use(session({
secret: 'SomeSecretKey',
key: 'session_id',
resave: false,
saveUninitialized: false,
cookie: {maxAge: 24 * 60 * 60 * 1000, secure: false},
store: new redisStore({
host: config.redis.host,
port: config.redis.port,
client: redisClient,
ttl: 86400
}
),
}));
My route /api/session looks like the following in NodeJS: (I'm manipulating the session object and append some values to it)
app.get('/api/session', (req, res) => {
console.log(req.sessionID);
req.session.test = 42;
res.end('1');
});
When I go to localhost:3000/api/session via Browser, I get the following on my redis-cli monitor:
Like you see in the screenshot - the session ID is keeping the same.
But when I do the GET Request from Angular HTTP Module via:
then my redis-cli monitor shows the following (it is generating a new sessionID for every request, and I can not store any data inside the session):
So summarized: GET Request from browser DOES store my session, but from Angular HTTP module the GET Request DOES NOT store my session. Further with Angular GET Request it DOES NOT store the session_id in my application cookie (when I press F12 in Chrome).
EDIT:
When I do curl localhost:3000/api/session it does the same like Angular HTTP module: New session for every request. I want to have a persistent session with the same sessionID.
What did I do wrong?
Thanks in advance
For everyone who has also run into this issue: after hours of reading, I found this thread, where user FlavorScape posted his accepted answer with the hint at the end of his code example. The following steps solved my issue:
1 I've changed the session in NodeJS to the following options:
app.use(session({
secret: 'SomeSecretKey',
key: 'session_id',
resave: true,
saveUninitialized: false,
cookie: {maxAge: 60000, rolling: true,},
store: new redisStore({host: config.redis.host, port: config.redis.port, client: redisClient, ttl: 86400}),
}));
2 Set the Access-Control-Allow-Origin in NodeJS to the origin of your frontend application:
app.use(function (req, res, next) {
res.header('Access-Control-Allow-Origin', 'http://localhost:4200');
res.header('Access-Control-Allow-Credentials', true);
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
next();
});
3 That's the most important part I guess: In Your Angular application, set withCredentials: true in the GET options
getUserSession(): Observable<any> {
return this.http.get(this.config.ENDPOINT_URL + 'session', {withCredentials: true});
}

Can't make lusca CSRF work with https: 403 forbidden

This is driving me nuts. I have tried reading the lusca source code but found it hard to understand.
Checked several examples too, but since each config is different, and the only debugging output I have are two strings to compare, I'd better ask for some help!
Here's the code server side:
app.use([
cookieParser(process.env.SESSION_SECRET),
session({
resave: false,
saveUninitialized: true,
secret: process.env.SESSION_SECRET,
store: new MongoStore({ url: MONGO_URL, autoReconnect: true }),
cookie: {
secure: process.env.NODE_ENV === 'production'
},
}), lusca({
csrf: true,
xframe: 'SAMEORIGIN',
xssProtection: true,
})]);
And from the clientside, I send Ajax POST requests with the x-csrf-token:l0gH3xmssge53E/p2NsJ4dGnHaSLdPeZ+bEWs= header in it:
fetch(url, {
method: 'POST',
credentials: 'include',
headers: {
'x-csrf-token': CSRF_TOKEN
}
});
Crazy thing is, it's working locally, but as soon as I go https in production, I get the 403 Forbidden error message.
Here are the versions I use:
"cookie-parser": "1.4.3",
"express-session": "1.15.3",
"lusca": "1.5.1",
Also I read this from the express/session doc:
Note Since version 1.5.0, the cookie-parser middleware no longer needs to be used for this module to work.
But as far as I'm concerned, I need to store some persistent ID of the users (longer than the session). I need to use cookies for that, right?
I'd like to understand better on the whole session/cookie thing, but until now I never found any useful resource on the topic.
Thanks!
If you are running your Node.js server behind a proxy you will need to set trust proxy to true:
var isProductionEnv = process.env.NODE_ENV === 'production';
app.use([
cookieParser(process.env.SESSION_SECRET),
session({
resave: false,
saveUninitialized: true,
secret: process.env.SESSION_SECRET,
store: new MongoStore({ url: MONGO_URL, autoReconnect: true }),
proxy: isProductionEnv,
cookie: {
secure:isPrudictionEnv,
},
}), lusca({
csrf: true,
xframe: 'SAMEORIGIN',
xssProtection: true,
})]);
app.set('trust proxy', isProductionEnv);
Check out this stack overflow answer. Also check out this page on Express behind proxies.

Resources