Cookies handling when client-side is also an ExpressJS app - node.js

As far as I'm concerned, for a server side application to know what clients are communicating with it, it will save a cookie in the client with the session ID.
That is indeed what express-session a popular package for session storage in ExpressJS says in the documentation
Note Session data is not saved in the cookie itself, just the session ID. Session data is stored server-side.
So I believe I can assume this is strategy used by Express to maintain user data in sessions as well.
I did something else: I'm using Redis to store the Session data in my ExpressJS server app.
So having said that, my problem is that my client application is also an Express app. I have a nodejs app with Express both for client and server. There is a SPA involved in this problem, but it communicates with the 'express client' so it appears to be a different problem.
So everytime I send a request from Express Client to Express Server, there is not cookie being passed, so it can't identify a session ID and creates a new one, generating lots of garbage in Redis and making the session solution useless.
How should I save or fake a cookie in the Express Client app, or, even better, how this kind of problem is solved?
Actually if I knew what the Express Server is expecting in the request (a cookie, a header or whatever) I think I can solve the problem.
Anyone know what to do?
#Solved
Alright, so, in my nodejs client application I did the following:
login(req,res,next){
var options = {
url : 'http://localhost:8090/user/login_handler';
};
request(options, function(error,response,body) {
var cookie_string = response['headers']['set-cookie'][0].split(';')[0];
req.session.cookie_string = cookie_string;
});
}
check(req,res,next){
var options = {
url : 'http://localhost:8090/user/check_handler',
headers: {
'cookie':req.session.cookie_string
}
};
request(options, function(error,response,body){
res.json( body);
});
}
In short, when the session is created in the server side, it will respond with headers to tell the client to create a cookie. I save the important information to pass as a cookie in a different moment. The server-side then read the headers in the middleware and load the correect data to the session.

Without knowing the details of your architecture I would guess that what you want is to either set the saveUnitialized option to false, and not save the extraneous sessions, or only apply the express-session middleware to certain routes in your "Express Server" application

Related

Not able to receive/set cookies in browser from backend in MERN app with backend hosted on heroku and frontend on netlify

I have a MERN app whose backend is hosted on Heroku and frontend on netlify. I am not able to see the cookies in the browser after deploying the app but it works fine on localhost. I think it is due to different backend and frontend ports, what am I missing, please help
You are correct. Cookies are not cross-domain compatible. If it was, it would be a serious security issue. The easiest way to fix your problem would be to send back the cookie as a res object, and then setting the cookie manually in the frontend.
Take this for example. I'll do this with JavaScript style pseudocode. Don't copy paste this as this most likely wouldn't work right away. This is what you're going to do on the back-end:
// 1. Authenticate the user.
const userData = await authenticateUser();
const userToken = await verifyUser(userData);
// 2. Return the response object.
return response.status(200).json({
status: 'success',
data: userData,
token: userToken,
});
In the front-end:
const response = await axios.post(...); // your API call, will return above object.
// set your authentication token here.
// the 'options' object will contain all possible cookie options, example would be 'secure'.
cookies.set('token', response.data.token, options);
// alternatively, set the cookie in the local storage.
localStorage.setItem('token', response.data.token);
You need to set the cookie accordingly in the front-end.
Further reading: MDN Docs
EDIT: Your question is unclear. First time you talked about cookies, but now you're talking about httpOnly cookies. Please be more specific in your questions.
It is impossible to set httpOnly cookies in React.js if it is cross-domain. React is only responsible for the 'view' of the website. httpOnly cookies are only meant to be set server-side. Client-side JavaScript cannot read or set that specific cookie, but it is able to send it. Unless you have something in your Netlify that can do server-side operations, I don't think that is possible.
Your best bet is to actually just use the same domain.

Store session in client's browser using raw node.js and cookie without express

I am trying to store client session using raw node.js without express.
When a user logs in I have the username and password. Now, how to store a session in client browser using cookie. And how to identify the user when they refresh the tab or goes to another link.I don't want to use client-sessions module as I want to understand the approach.
any help will be appreciated.
First of all, I suggest you to watch everything about authentication in NodeJS It explains cookies in a part very well.
You have to give the browser some data to hold for it to use later, which being cookies. Browser uses this data to show the server what kind of authentications it has processed before for the server and the user to proceed without repetition.
In node.js, using client-sessions module, you can set a cookie by calling
app.post('/login', function(req,res){
User.findOne({password: req.body.userPassword}, function(err, user){
if(user){
req.session.user = user; //here you are setting the cookie for the client,
}
})
})
You could also specify what kind of cookie you want to set by just adding it a property
req.session.userEmail = user.email;
Now let's check how the server uses authentication
app.get('/someUrl', function(req,res){
if(req.session.user){
console.log("user exists!");
}
})
You can check what the client sends you by using session property of req object => req.session
To bind it with database you need to do,
if(req.session.user){
User.findOne({email: req.session.user.email}, func...)
}
So basically, with each request that client sends, this procedure is used by the server to identify the cookies and to make the web-app user-friendly with no repetition.
It is like giving every student an ID in a school for authentication.
Security
For security, the node module cookie-sessions encrypt data automatically when we add secret attribute in app.use() function. Please see using secret in client-sessions module

Meteor: Endpoint for images fetched from S3 - How to authenticate connection?

We have encrypted images stored in S3 that we need to serve to clients, meaning that we cannot give clients S3 URLs for the img src. The files are also potentially large, so idealy we would like to not go through js.
With no serverside routing available we were going down the route of having a separate express setup in Meteor, and this works, since the router on the client side doesn't interfere with
We could add the Auth token to the src url and poke the DB, but we're wary of doing so as it would expose the token in the DOM and in copy pasta cooked up by the users.
Is there a good way of getting this to work properly? Is it posible to configure other routers to serve up the angular app on a specific URL maybe?
Any input welcome :)
app = Express();
app.get('/order/:orderID/image/:UUID', function(mainReq, mainRes) {
// TODO: security check, but not getting current loggedin user info
// There are no cookies, only the DDP connection is authenticated (?)
console.log(Meteor.userId()); // fails
// S3 fetch and decrypt here
});
The answer is:
It's not possible using out of the box Meteor. If you wanna restrict HTTP requests, you're on your own.
Meteor doesn't use cookies (on purpose and for good reason; https://blog.meteor.com/why-meteor-doesnt-use-session-cookies-e988544f52c9), but instead only ever authenticate the DDP websocket connection, and hence any HTTP request to the server is not authed in any way. There are a few packages that tries to handle these things, and this article explains a way of putting the auth token (and the user ID) into the url: https://blog.kayla.com.au/server-side-route-authentication-in-meteor/
The problem with this is that you then expose the token in the DOM, and any browser extension would be able to read it, and the user would be able to copy/paste the url and send it to others. This could end up in session hijacking.
If you wanna authenticate HTTP requests, you will have to wither find a package that write a cookie (and prevents CSRF attacks if you're doing actions) or have the user supply the username/password each time.
For my situation it is sufficient to have the client side write a cookie with the auth token on login on the client. It will then be sent with the request and can be checked server side. Since all I'm doing is send back a picture, it's not nessaccary to prevent CSRF for me, so beware of that while reading the snippets below og how to have the client send a cookie to the server:
Accounts.onLogin(() => {
removeExistingCookie(cookieName);
document.cookie = "loginToken=" + localStorage['Meteor.loginToken'] + "; domain=" + document.location.hostname + "; path=/; expires=" + expiryTime + ";secure"
});
Then you'll have to parse the cookie header on the server and auth the request on the server using something like this
let cookieParser = Npm.require('cookie-parser');
app = Express();
app.use(cookieParser());
app.get('/order/:orderID/image/:UUID', function(mainReq, mainRes) {
let loginToken = mainReq.cookies["loginToken"];
if (!loginToken) {
mainRes.status(404).send();
return;
}
let hashedToken = Accounts._hashLoginToken(loginToken),
sessionBelongsToUser = Meteor.users.findOne(
{
'services.resume.loginTokens.hashedToken': hashedToken,
});
if (!sessionBelongsToUser) {
mainRes.status(404).send();
return;
}

Sending a cookie to web browser using Node.js Express

I am following all the suggestions online about how to send a cookie in an HTTP response in Express.
The simple way would be like so:
app.use(function(req,res,next){
res.cookie('foo', 'bar');
});
however when future requests come to the server from the same web browser, the cookie is not available in req.cookies; in other words, req.cookies['foo'] is undefined.
I have set the front-end code to use "withCredentials" for any AJAX request. Yet the cookie is still not sent to the server, or at least does not appear to be in any way.
One thing that concerns me is that when I call res.cookie('foo','bar') on the server it doesn't show up on the res object/stream as you can see here:
https://www.dropbox.com/s/fe317re9g2frucv/Screenshot%202016-01-21%2023.49.45.png?dl=0
Is there anything obvious that I am doing wrong?

express.js + socket.io - how to initialize session on server from client app

I have to create 2 apps. One app is a server using express.js and socket.io. Second app is a simple client (static html+js file) and must not be served from server express.js instance. The cookie express.sid witch is used to establish session is created only when express.js route is accessed.
My problem occurs when I'm connecting from client (ie. http://client.addr) to server (ie. http://server.addr:0000) using socket.io - I can't establish express.js session because I didn't accessed the server route and I don't have express.sid session cookie.
I ended up with client trying to send AJAX call to server /set-session-cookie route in hope I can identify session, send back cookie with encrypted sessionID and than start socket.io connection hooking in to session that was created in previous call but no cookie is sent back after AJAX call.
Server route:
app.all('/set-sesion-cookie', function(req, res){
res.header('Access-Control-Allow-Origin', req.headers.origin);
res.header('Access-Control-Allow-Credentials', 'true');
res.cookie('express.sid2', req.sessionID, {signed: true});
res.send(200);
});
Client call:
$.post('http://127.0.0.1:3000/set-sesion-cookie');
Second thought was to send back only string containing encrypted session id and create cookie using JS but I can't find out what the encryption algorithm is for res.cookie('express.sid2', req.sessionID, {signed: true}); to do it manually.
Is this approach valid? If there is another way to achieve this please let me know. TIA.
IMHO session cookies should be HTTP-Only for security reasons.
This means there is no scripting support for this cookie.
You can avoid the Same-Origin-Policy problem by serving static files w/ nginx and reverse proxy node.js app behind nginx. This way all comes from one server and you don't have problems sharing a cookie/session-id.
AFAIK Apache httpd is not able to proxy WebSocket calls.
Another possible workaround instead of nginx might be HAProxy.

Resources