Session data lost between calls (Node/Express) - node.js

Right now my problem boils down to two servers. One node server is just serving up my static assets (HTML/JS/CSS). Another server is acting as an api - it's a rudimentary start to a microservices approach. I'm using PassportJS to handle authentication and Redis as a session storage.
When I login, I can see that my session data is being stored in req.session, but in every subsequent request, that session data is gone. The PassportJS documentation suggests that as long as I'm calling passport.initialize, the req.session.user object is created upon every request (with session data stored in the user object by invoking passport.session). However, the req.session.user object does not exist, and the only time it seems to appear is after I call req.login (and by then it has the user's data stored).
This is my middleware implementation inside the API server:
app.use(cookieParser());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use(session(sessionConfig));
app.use(passport.initialize());
app.use(passport.session());
An example of what req.session contains after req.login is invoked
{ cookie:
{ path: '/',
_expires: null,
originalMaxAge: null,
httpOnly: true },
passport: { user: <some user id> } }
What req.session contains on any request, including login prior to explicitly calling req.login
{ cookie:
{ path: '/',
_expires: null,
originalMaxAge: null,
httpOnly: true } }
Is there something I can do to salvage all this PassportJS code? This setup seemed to work fine prior to splitting the static asset handling into it's own server. It also seemed to work fine though when I was using NGINX (which I got rid of because of my lack of comfort with a non-scripting tool) to serve up my static assets instead of a node server.
I have an inclination that I'll probably have to roll my own authentication and explicitly pass the sessionid every time I make a request from the client, but I'd prefer not to in case I'm missing something here.

Related

Can fs.writeFile called in a file routed to express, middlewared by session, delete the session cookie?

I routed a js file to the main js file using express and it's router middleware. Also each path is middlewared by session. After authentication, a get request which is received only when req.session.loggedIn exists, will execute on every request since the login.
But on a put request, it will execute once and then will be blocked by the authentication validator which is a middleware in both requests.
When commenting out the fs.writeFile block the cookie remains as expected, and put request are executed more than once.
Can fs.writeFile mess up cookies?
This is the problematic block:
fs.writeFile('./data/tasks.json', stringed, (err)=>{
if(err){return console.log(err)}
console.log('req.session from tasks put:',req.session)
}, ()=>res.send(`Successfuly added task. `))
This is the validation middleware:
const validator = (req, res, next)=>{
console.log('req.session in validator:',req.session)
if(!req.session.loggedIn){
return res.status(401).send('Please log in.')
}
next()}
This is the session middleware:
app.use(session({
secret: ';klmkljhjkhn;jk',
cookie: {
maxAge: 6000065456468476813541684864764561231,
httpOnly: true,
secure: false,
},
saveUninitialized: true,
resave: true,
}))
This is the routing middleware:
app.use('/tasks', validator, require('./routes/tasks'))
The problem that you describe is not easily exhibited by the code in your repository that you linked to in the comments. I was able to login and add 2 tasks successfully via POST, and then read them back, without losing the session data.
It is possible that you were having some timing-related issue with the 2 callbacks: fs.writeFile() takes 1 callback, so the other callback was never being executed; I was unable to verify this, since the code in git does contain this exact statement. Since express-session saves the session only when the response is sent, and it didn't happen here, it was likely timing out - so it's probable that:
You started a request to POST /tasks - it loaded the empty session (not logged in)
You then logged in, and that response finished
Finally, the first request to POST /tasks ended - and express-session resaved the (empty) session, so it overwrote your logged-in session
This can be avoided if you disable resave: true.
To sum up: fs.writeFile is not causing session problems by itself, but can lead to requests which overwrite session state, especially in the presence of:
callback bugs
very long writes
resave: true
The solution to my problem was disabling nodemon, which kills the server on each file change, which is what fs.writeFile just does. I'm so relieved!

Passport: How to use authenticated user on server1 and server2?

I want to share the passport authentication across multiple servers...
Here is my situation:
Server1: (app1.domain.com) authenticates user with passportjs local strategy. using cookie-session
Server2: (app2.domain.com), I want to leverage the user authentication from server1.
I am using the cookie-session middleware like so:
app.use(
session({
domain: `.${config.baseDomain}`,
cookie: {
path: '/',
domain: `.${config.baseDomain}` ,
maxAge: 60000,
},
secret: config.secret,
signed: true,
resave: true,
})
);
On Server1, the resulting req.session object is one line:
{ passport: { user: '5fdb8088ab37a78b980c2e6f' }
On Server2, req.session looks more similar to the req object, with hundred of lines.
I thought that using the same cookie-session middleware would mean that the req.session object would be identical?
I am trying to understand each step, because I am not sure where it is breaking.
The reason the req.session object was different on Server1 and Server2, is because of something that took me a while to track down, though it's painfully simple.
After isolating cookie-session as the culprit, I finally looked at my package.json to find servers had the different versions of cookie-session installed. (2.0Beta & 1.4.0) Once I installed v1.4.0 on both servers, everything works as expected.

node.js express session access token doesn't persist after changes

I'm quite new to OAuth and not sure what to do with the access token I receive from another party. Right now I'm using express session on https with secure and httpOnly settings. This works fine, until I upload an image on the same API server (which happens after I add a product). Everytime my server detects changes, the token I saved becomes undefined, this means that the user has to go through the whole OAuth process again.
Since I use MYSQL, is it possible to save the token information in the database (expiry, refreshtoken, accesstoken) linked to the user or is there a better way to save this data?
My setup is very basic, I have one API Server and one React app for front-end.
I receive the token information by making an API call with my own API to the other party, the response from this party is what I end up sending as cookies to the React app.
This is the session code I have right now:
app.use(
session({
secret: process.env.SESSION_SECRET,
name: "token",
cookie: {
secure: true,
httpOnly: true,
},
resave: true,
saveUninitialized: true,
})
);
For anyone that runs into the same problem, by default express session uses MemoryStore. I missed this when I was reading the documentation.
MemoryStore is purposely not designed for a production environment. It
will leak memory under most conditions, does not scale past a single
process, and is meant for debugging and developing.
To fix this, you can use either "cookie-session" or pick one of the stores from the documentation.
https://www.npmjs.com/package/express-session#compatible-session-stores

Sharing Redis Sessions Across Node Apps

I'm busy building a platform with 3 different subdomains - example.com, auth.example.com and api.example.com. They're run with 3 separate NodeJS apps running on different ports of the server.
Here is the code setting up the sessions:
var session = require("express-session");
var redisStore = require("connect-redis")(session);
var redisClient = require("redis").createClient(config.redis);
app.use(session({
secret: config.server.secret,
store: new redisStore(config.redis),
client: redisClient,
resave: false,
saveUninitialized: false,
cookie: {
domain: "example.co.za",
httpOnly: false
}
}));
The configuration is exactly the same for all 3 apps and they're sitting on the same server. For some reason, the sessions are not being shared. I seem to remember that they were being shared a few weeks back and now things are broken - I have a sneaky suspision that this happened when we moved all the traffic from HTTP to HTTPS. Would this break the sessions? I tried to turn of 'httpOnly' in case it restricted the sessions, but no luck.
I have run redid-cli MONITOR and the session is, in fact, being saved on login (Auth App) but is not being retrieved by the other app. When I turned saveUninitialized to true, the requests to save were coming from all 3 apps - this shows that they are connected to the same Redis Store.
Any help would be great.
I think this is just a cookie issue. The browser is not sending the session cookie back on your sub-domains:
you need a leading . on the domain. e.g.:
cookie: {
domain: ".example.co.za",
httpOnly: false
}
In case that doesn't work and you are having AJAX issues see this post

Node Express - difference between req.cookies and req.session.cookie

I am trying to find out what the difference is between req.cookies and req.session.cookie. I am using Passport for authentication in Node.js Express.
If I log these two lines in my code:
console.log('cookies',req.cookies);
console.log('session',req.session);
I get this output:
cookies { 'mysite.sid.uid.whatever': 's:Ltko5IdDgsAISG0smrKNYaeIVy8nbBzF.MkGmpnf6uUKITIAgN4ws3YXqxJrMaeeSCzlKdjQnqfI' }
session { cookie:
{ path: '/',
_expires: null,
originalMaxAge: null,
httpOnly: true,
secure: false },
views: 8,
passport: {} }
I am using this configuration:
app.use(bodyParser.json());
app.use(bodyParser.urlencoded());
app.use(busboyBodyParser());
//app.use(busboy());
app.use(cookieParser('cookie parser secret'));
app.use(session({
secret: process.env["SESSION_SECRET"],
saveUninitialized: true, // (default: true)
resave: true, // (default: true)
store: require('mongoose-session')(mongoose),
maxAge: 60000,
key: "mysite.sid.uid.whatever",
cookie: {secure: false}
}));
I don't really know the difference between using sessions or cookies, except that cookies are client-side only and sessions could be either client or server-side. I have read the documentation from Passport.js a few times, but I still don't really understand what is going on here. Can someone help me out with some explanation? As far as I can tell, it seems best to use server-side session using Redis. But I don't see how you can get away from using client-side data in the end. At some point, you have to rely on the client-side data stored right?
after I login with the express app, the passport object gets populated with a user field, with the MongoDB objectid.
passport: { user: 549290b8246f0e1408e48b13 } }
Typically you will be using cookies when serving browsers. The exception to this being authenticating via an HTTP header or POST parameter token, which are more typical for API requests for example.
It is true you can do client side or server side sessions using cookies, where the entire session data is stored in the cookie in the former (and does not use any storage server-side) or session data is stored server-side (with a session ID stored in a client-side cookie) in the latter.
req.cookies contains cookie values only, no matter if the cookies are session related or not. req.session.cookie contains the Set-Cookie parameters used in the client-side session ID cookie.

Resources