Having troubles sharing sessions between subdomains in express - node.js

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!

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

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});
}

Express session is empty in Safari

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();
});

Renewing/Refreshing Express Session

In my app I restrict some access to some actions and pages if a user is not logged in. I have:
var restrict = function(req, res, next) {
if (!req.user) {
console.log("USER isn't logged in.")
return res.status(403).send('Access or action denied, please log in');
}
next();
}
app.get('/stocks', restrict, MainHandler.findAllStocksFromUser);
app.get('/stocks/:id', MainHandler.findStockByIdAndDates);
app.put('/stocks/:id/stockActions', restrict, MainHandler.handleStockAction);
I'm essentially trying to refresh a session everytime the client makes a request to the server so that the server doesn't logout the user/destroy the session when it shouldn't. For testing, I want the session to expire/the user to be logged out if 20 seconds go by without the user making an requests to the server. I have:
app.use(session({secret: 'secret', saveUninitialized: true, resave: true, expires: new Date(Date.now() + (20000))}));
Then I try to use middleware to refresh the expiration date every time the use makes a request:
// Session-persisted message middleware
app.use(function(req, res, next){
req.session.cookie.expires = new Date(Date.now() + 20000);
next();
});
But if I log in from the client, and click around, causing requests to the server, I still get the log-in error on the client after 20 seconds, despite trying to "refresh" the session in the middleware. I have also tried using maxAge using the same strategy with the middleware. Any ideas? Thanks!
You can try define your session as follows
app.use (
session ({
secret: "secret",
saveUninitialized: true,
resave: true,
cookie: {
expires: 20 * 1000
}
})
);
and then refresh the session using
req.session.touch()
or you could define your session as
app.use (
session ({
secret: "secret",
saveUninitialized: false,
resave: true,
rolling: true,
cookie: {
expires: 20 * 1000
}
})
);
and it will renew the session automatically and it will only expire when it has been idle for the value in the expires variable
express-session supports a duration-based maxAge setting, which will work better than setting a fixed date for all sessions. So your middleware usage should instead look like:
app.use(session({
secret: 'secret',
saveUninitialized: true,
resave: true,
maxAge: 20000
}));
Next, to update the expiration of the session, you can just call req.session.touch(); if that is all you're doing to the session and its contents.
The documentation has a lot of other good information on controlling session expiration and related topics.

Resources