How to remember data of the client? - node.js

I have some information that is unique to each client. This is not the login information but just some generic data of the client - for example, say the address of the client.
Now, when a client connects with the server for the first time, I will read the address of the client.
Subsequently, each time the client makes requests to the server, I would like to use the address that I read from the first request.
How do I do this? I mean, how do I store some information on the client in the server itself so that with each request, I do not have to read the address again (from the DB) but instead read it directly from say, a global variable or a like a request header or something on those lines... How, do I attach the address to each client so that for future requests from the client, I can directly read the address information from the client's request itself without having to query the DB once more...

edit - this requires Express, didn't notice that your question was not tagged express. if you aren't using express you may want to look at the connect.session example, this is what express uses behind the scenes to populate req.session.
Depending on exactly what you want to do with the data, you might prefer to use req.session to store temporary information until the client closes the window or their cookie times out. Here's an example that keeps track of the address across future requests:
var express = require('express');
var app = express();
app.use(express.logger());
app.use(express.cookieParser());
app.use(express.bodyParser());
app.use(express.session({secret:'$tackoverflow-rules'}));
app.get('/', function(req, res){
if(req.session.address === undefined){
res.send(200,'<html><body><form action="address" method="post">'
+'<input type="text" name="address" placeholder="Enter address">'
+'</form></body></html>');
} else {
res.send(200,'<html><body><span>I know that you live at '+req.session.address+'!</span></body></html>');
};
});
app.post('/address', function(req, res){
if(req.body.address !== undefined && req.body.address !== ""){
req.session.address = req.body.address;
res.redirect('/');
} else {
req.session.address = undefined;
res.redirect('/');
};
});
require('http').createServer(app).listen(3000);

You can store the information in cookies or localStorage if the data is being displayed on a webpage. If you are using a jsp or php I would store it in a session. What client are you using?

You can create a global object with a clients address as the key that stores info:
var client = {}
//on connect
client[clientID] = {address: clientAddress} //so on
//To access
client[clientID].address; //or whatever you need
This is how I store client specific information with socket.io

If the data is small you can assign the data to a cookie and that way it will be passed back and forth on each request to the server (Localstorage will allow it to keep it on the client but it will NOT be be passed again to the server on the next request)
You can keep a collection in memory with this data on the server and look it up as #tymeJV indicated but this will only works if you have the data is not too big and if you run in a single server. As soon as the data grows you might run out of memory. Also if the server restarts you'll loose this information. As soon as you add a second server this approach will not work because you'll need to make sure the client connects to the same server (which I guess you could do with sticky sessions)
You might want to look into caching this data with something like Redis or another fast database like MongoDB for this kind of session information. That way you'll have a central store for it that could be optimized for performance so your site does not get bogged down while checking for this information.

Related

Sharing a variable between 2 routes express

I wanted to know if there is a way to share variables between 2 routes in expressJS without declaring it as a global. Consider I have the following routes:
exports.routeOne = (req,res) => {
var myVariable='this is the variable to be shared';
}
exports.routeTwo = (req,res) => {
//need to access myVariable here
}
How can this be done ?
===============
Note
This answer is answering the wrong question, but it might still be useful for others who also misinterpreted the question like I did, so I'm keeping it here for now.
===============
In order for the variable to exist it would have to first execute routeOne and then execute routeTwo. This is a pretty common scenario with express. To get the details on how this works read up on middleware (http://expressjs.com/en/guide/writing-middleware.html) and understand that each route is middleware.
The common pattern solution here is to add a new property to either the req or res object that stores your variable. Then you tell the route to call the next middleware. The next middleware has access to the same req and res so it also has access to the property and value that you just stored.
There is nothing wrong with this practice as most middleware does it. For example the body-parser middleware (https://www.npmjs.com/package/body-parser).
Here is an example of how your code might run:
routes.js
exports.routeOne = (req,res,next) => {
req.myVariable='this is the variable to be shared'
next()
}
exports.routeTwo = (req,res) => {
//need to access myVariable here
console.log(req.myVariable)
}
index.js
const express = require('express')
const routes = require('./routes.js)
const app = express()
app.use(routes.routeOne)
app.use(routes.routeTwo)
app.listen(3000)
One of the awesome things about NodeJS is that you can store state in variables. The process that runs your NodeJS server continues to run without restarting for each request that comes in. So any variables you store using GLOBAL.myVarName or variables that you store within the scope of a module (a single JavaScript file) will persist... until the process quits. The process could quit if the server goes down or if an Error or other exception is throw that is not caught. You can set up a process manager to keep it running, but your in memory variables are now lost.
I would recommend using variables that are scoped at a higher level for caching, but if the data is important it should be stored to and read from a database as needed.
Here is an example of your routes.js module. Any variable you declare outside of functions are scoped to this file. In other words, the variables are global to this file. You can read up more on that at https://nodejs.org/dist/latest-v8.x/docs/api/modules.html and https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures.
routes.js
let variableScopedToModule = 0
exports.routeOne = (req, res) {
variableScopedToModule++
}
exports.routeTwo = (req, res) {
console.log(variableScopedToModule)
}
If it is important that you never loose the variables state, and assuming you are not running the server on multiple processes for load balancing, then you could do something like this:
routes.js
const db = require('./my-db-controller')
let cachedValue
exports.routeOne = async (req, res) {
if (cachedValue === undefined) cachedValue = await db.getValue()
cachedValue++
db.setValue(cachedValue)
}
exports.routeTwo = (req, res) {
console.log(cachedValue)
}
This second solution is more complex because you'll need to understand promises as well as async and await. Also this solution will not work if you are using a load balencer unless you use something like Redis. Alternatively you can just read and write to the database for each request instead of using in memory caching.
For user-specific data that you want to persist from one request to the next, you can't just store data in simple server-side variables because server-side variables are freely shared among all users who make requests of your server.
The usual way to store server-side data that is user-specific is to use a session object. In a nutshell, when a user makes a request, some session middleware (which runs on every request) checks to see if a session cookie exists. If it does not, then one is created. The session cookie servers to uniquely identify that particular user when they make future requests.
Once you have a unique session cookie, the session middleware can create a session object that corresponds with that session cookie. Typically, there is a unique ID in the session cookie that servers as a lookup key for the session object.
Now, whenever that user makes a request of your server, the session middleware will look at the session cookie, get the encrypted session id out of it, decrypt the id, look up the session object that corresponds to that id and then make that session object available to your request handlers, usually in req.session. Your request handlers then have full access to the session object and can add, remove or modify your own properties on that session object.
express-session is common piece of middleware that is used for implementing sessions with nodejs/express. There are plenty of examples for how to use it in the documentation so I won't repeat that all here. Here's one such example in the doc.
By default, express-session uses a memory store (which just means the session objects are kept in memory). This can be fine for simple uses, but it has some limitations and express-session does not recommend it for production use. One major limitation of keeping the sessions in memory is that if the server crashes or restarts, all session data is lost. There are dozens of add-in object stores for express-session, most of which store data more durably such as in a database on disk.

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

Cookies handling when client-side is also an ExpressJS app

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

Working with Sessions in Express.js

I need help understanding the concept of sessions for a web application. I am running a Node.js server with Express 3.0.
My goals are to:
Create a session for each user that logs in
Store this session and use it for validating if the user is already logged in (prevent two devices using the same user at the same time) and to limit access to certain pages (by matching session ID to some other data)
I will be using MemoryStore to save the sessions (seems easiest). If the above goals make sense can you provide a thorough explanation of how to achieve them?
Express has nice examples in the github repo. One of them deals with authentication and shows how to attach the user to the req.session object. This is done inside the app.post('/login') route.
To limit access to certain pages add a simple middleware to those routes
function restrict(req, res, next) {
if (req.session.user) {
next();
} else {
req.session.error = 'Access denied!';
res.redirect('/login');
}
}
app.get('/restricted', restrict, function(req, res){
res.send('Wahoo! restricted area, click to logout');
});
As Brandon already mentioned you shouldn't use the MemoryStore in production. Redis is a good alternative. Use connect-redis to access the db. An example config looks like this
var RedisStore = require('connect-redis')(express);
// add this to your app.configure
app.use(express.session({
secret: "kqsdjfmlksdhfhzirzeoibrzecrbzuzefcuercazeafxzeokwdfzeijfxcerig",
store: new RedisStore({ host: 'localhost', port: 3000, client: redis })
}));
Use MemoryStore in express ONLY if you are not creating multiple instances (such as with the cluster module). If you are load balancing across machines, your load balancer will need to use sticky / persistent sessions.
If you meet those requirements, then all you need to do is upon login, once the credentials are validated, set a session variable to indicate logged in, for example:
req.session.loggedIn = true;
If you want to check if a user is logged in, simply check that variable.
if (req.session.loggedIn) {
// user is logged in.
}
else {
// user is not logged in.
}
You mentioned preventing a single user from having sessions more than one session at a time. To achieve that, you may need to store something in a database indicating that the user is logged in. I warn you, this can be dangerous because of stale sessions. For example, what if a user logs in, but never logs out? What if they close their browser window so the session is gone forever?
Express has no concept of an idle session expiration. I have implemented such a thing by storing all sessions in the database along with a last accessed timestamp and then periodically clean up the session data based on the time. But then you need to update your list of who is logged in as well.

Resources