Google App Engine connection.session() error - node.js

My App Engine logs show this.
Warning: connection.session() MemoryStore is not
designed for a production environment, as it will leak
memory, and will not scale past a single process.
Now my nodejs App can't use session. How I fix this?

Assuming you're referring to user authentication, note the following portion of the code from the AppEngine Node documentation and example:
// In production use the App Engine Memcache instance to store session data,
// otherwise fallback to the default MemoryStore in development.
if (config.get('NODE_ENV') === 'production' && config.get('MEMCACHE_URL')) {
sessionConfig.store = new MemcachedStore({
hosts: [config.get('MEMCACHE_URL')]
});
}
The default MemoryStore fallback is basically just there for development purposes; you should specify a more permanent/scaleable session storage of your choice for actual usage.

I think you are using PM2 or your server is running multiple threads, with default session storage mechanism. This won't scale pass a single thread solution, which is what you generally use in the development mode.
Hence, in-order to persist the session, you need to store it somewhere. For example, Redis.
const EXPRESS = require('express');
const APP = EXPRESS();
const EXPRESS_SESSION = require('express-session');
const REDIS_STORE = require('connect-redis')(EXPRESS_SESSION);
APP.use(EXPRESS_SESSION({
secret: 'YOUR_SECRET',
saveUninitialized: false,
resave: false,
store: new REDIS_STORE({ //storing the session in redis
host: 'localhost',
port: 6379, //redis port, should be 6379 by default
ttl: 300 //time-to-live, session will be destroyed if no activity in 5 mins
})
}));
Source of the code: Personal project

Using a session store fixed the error.
use this link.
https://github.com/expressjs/session#compatible-session-stores
In the README.md under
compatible session stores there is list of compatible session stores for expressjs.

Related

Redis session appears to reset when (free) Heroku dyno sleeps

I'm hosting a Node.js server on the free tier of Heroku (for now), and I'm using Redis to store the user login session (with connect-redis in express). The redis database is on the free tier of Redis Cloud (on Redis Labs, so NOT on Heroku).
The issue is each time the Heroku dyno sleeps and wakes up, my users are all logged out, despite the cookie age being months long.
Here is the only place I touch Redis in the code:
/// ... other imports
// Connect to Redis
const redis = require("redis");
const RedisStore = require("connect-redis")(session);
const redisClient = redis.createClient({
host: process.env.REDIS_HOST,
port: process.env.REDIS_PORT,
password: process.env.REDIS_PASSWORD,
legacyMode: true,
});
redisClient.on("error", (err) => {
console.log("Error: " + err);
});
// ...
// Set up session middleware
app.use(
session({
store: new RedisStore({ client: redisClient }),
saveUninitialized: false,
secret: process.env.SESSION_SECRET,
resave: false,
maxAge: 1000 * 60 * 60 * 24 * 120, // 120 days
})
);
Is it possible that somewhere when I initialize Redis or connect-redis it resets everything? I'm considering switching to JWTs however they aren't a perfect fit for this exact use case. What may be the cause of this issue and how can it be resolved?
In Heroku, the free tier of Redis database does not have persistence and will clear itself on shut down / reboots / etcetera.
Quoting from the documentation,
The hobby tier for Heroku Redis doesn’t persist instance data. If the instance must reboot or a failure occurs, the data on instance is lost.
The solution for this problem is to upgrade your Heroku instance, or if you want to stay at free tier, you may try other services, such as Redis on Cloud (Redis Labs). It provides a free persistence for 30MB of data in its free tier.
References:
Heroku Redis Documentation
Heroku Redis Add-On

How to destroy the session (store)?

For authentification I'm trying to understand, how sessions work. With help of documentation of express session and Sessions in Node JS I got it work.
Now I'm figuring out, what to do, that users can log out. In the documentation of express session is to read "The default server-side session storage, MemoryStore, is purposely not designed for a production environment." They recommand a compatible session store.
I have choosen connect-redis. They call it an "in-memory data structure store". Now I'm wondering, what is the difference between redis and the database, that I would like to use (back4app).
If I implement connect-redis
const RedisStore = require('connect-redis')(session);
const redis = require("redis").createClient();
let sess = {
store: new RedisStore({ host: 'localhost', 6379, client: redis }),
secret: cryptoString,
resave: true,
saveUninitialized: true,
cookie: {
maxAge: 1 * 60 * 1000,
},
}
server.use(session(sess));
the user object from back4app stills undefined. (Without redis the user object exists.)
As mentioned I have tryed Parse.User.logOut(). It doesn't work. The console says Parse.User is null.
Please, explain
what is the difference between back4app and redis? Do I need both?
how do I enable log out?
For all with the same problem. This is my other question in this context. It will help you to see the whole picture.

Multiple redis instances for multiple NodeJS servers

After a bit of research, I came to the conclusion that I can run multiple instances of Redis on my CentOS server for each NodeJS server I run (I use Redis to store sessions).
I have followed these instructions and both instances are running properly on two different ports.
On my NodeJS servers, I configured Redis as follows:
import * as session from "express-session";
var RedisStore = require('connect-redis')(session);
var redis = require("redis").createClient();
app.use(session(
{
secret: secret,
store: new RedisStore({ host: 'localhost', port: 6379, client: redis }),
cookie: { maxAge: 12 * 3600000 },
resave: true, saveUninitialized: true
}
));
One with port 6379 and the other with 6380.
I use req.session.regenerate to register a session.
Both login systems work perfectly individually. However, when I load anything on one application, the sessions of the other application are deleted (and need to be re-logged in).
What am I missing here?
The problem looks like it is the "session store" in Express and not your usage of Redis.
From the express session documentation:
NOTE be careful to generate unique IDs so your sessions do not conflict.
app.use(session({
genid: function(req) {
return genuuid() // use UUIDs for session IDs
},
secret: 'keyboard cat'
}))
Name: The name of the session ID cookie to set in the response (and read from in the request).
The default value is 'connect.sid'.
Specifically this warning explains your problem:
Note if you have multiple apps running on the same hostname (this is
just the name, i.e. localhost or 127.0.0.1; different schemes and
ports do not name a different hostname), then you need to separate the
session cookies from each other. The simplest method is to simply set
different names per app.

Using Redis for session store doesn't seem to work

adapting an existing app to Express 4.4.x
Trying to implement redis session store, using the following code:
var express = require('express');
var session = require('express-session');
var RedisStore = require('connect-redis')(session);
var redis = require('redis').createClient();
app.use(session({
store: new RedisStore({
host: '1.1.1.1',
port: 1234,
prefix: 'yourprefix:',
client: redis
}),
secret: '.......',
resave: true,
saveUninitialized: true
}));
However, when I run this there is no key stored in redis and I cannot call req.session. I'm sure it's something really simple I've missed or included something in the code which is restricting it. Could also be redis settings?
Thanks
Ok, I just got lost in the documentation. Difficult to keep track of express-session, redis, connect-redis and entirely remember the difference between everything.
The simple solution was:
var redis = require('redis').createClient(port, host, auth);
Now it works. I believe I was able to use .createClient(); when the redis server was hosted locally, however now we've moved to a dedicated redis server - these attributes are required.
Thanks, hope this helps somebody else!

Session updates not persisting between requests - nodejs/expressjs/redis/heroku

I'm using redis for storing my sessions in expressjs. It works fine when running locally and even works most of the time when deployed on heroku. The problem is that regularly (when deployed on heroku) I see my session updates being lost.
e.g. a user logs in to my website, and I add their user object to the session:
req.session.user = user;
however (sometimes) when I try and retrieve the object moments later (in a different request) it isn't there
//sometimes this is empty, even though I've just set it
var currentUser = req.session.user;
I initialise the session store as follows
if (process.env.REDISTOGO_URL) {
console.log('Connecting to redis:' + process.env.REDISTOGO_URL);
var rtg = require('url').parse(process.env.REDISTOGO_URL);
var redis = require('redis').createClient(rtg.port, rtg.hostname);
redis.auth(rtg.auth.split(':')[1]);
} else {
console.log('Connecting to local redis');
var redis = require('redis').createClient();
}
//sessions
app.use(express.cookieParser('secret key for cookie monster')); //used by session
var RedisStore = require('connect-redis')(express);
app.use(express.session({
store: new RedisStore({
client: redis
})
}));
Any ideas? Do I need to do some kind of flushing (or explicit saving) of the session data?
I'm going to hijack this ancient question and let you guys in on how I solved the problem.
TL;DR
You have probably copied the REDIS_URL value from your Heroku app config to your local .env file some time ago. Heroku changes that value over time and that has now actually happened.
The solution is to copy the new REDIS_URL value from your Heroku app config to your local .env file or use another Redis instance for local development.
What you probably already tried
Probably your sessions were working a few days ago and you tried and verified all the usual suspects in order to fix this sudden problem.
cookie: { secure: false }
'trust proxy'
credentials: 'include' + Access-Control-Allow-Credentials header set to true
All to no avail. Finally, when you remove the RedisStore bit from the session config, sessions are working again.
const session = require('express-session');
const RedisStore = require('connect-redis')(session);
// ...
server.set('trust proxy', 1);
server.use(
session({
// store: new RedisStore({
// url: process.env.REDIS_URL,
// }),
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: true,
cookie: {
path: '/',
httpOnly: true,
secure: false,
maxAge: null,
},
})
);
What's happening?
Sometime later I understood how removing the Redis store solved the problem. I was using a REDIS_URL in my local .env file that was provided by my Heroku app config. And I learned that the REDIS_URL on the Heroku app may change over time.
In order for Heroku to manage this add-on for you and respond to a
variety of operational situations, the REDIS config vars may change at
any time. Relying on the config var outside of your Heroku app may
result in you having to re-copy the value if it changes.
(emphasis mine)
So in my case that had actually happened and my local .env file still had the old REDIS_URL value. In effect, the session store could not be created, because it tried connecting to a service that isn't there. That's why removing RedisStore and thus falling back to the default MemoryStore fixed the problem.
Prevent reoccurrence
I created an account at redislabs.com and created a free Redis instance. That url I now use in my local .env file. That url doesn't change (I hope).

Resources