Express.js, GCP express-session deprecated req.secret; provide secret option at - node.js

Current development environment is as follows
Windows 11 Home-64bit
GCP Computer Instance: Ubuntu 20.04.5 LTS
Situation Description
Express.js + Sequelize is being used, and in the case of DB, GCP's Cloud SQL is being used.
In the local environment, it is determined that there is no problem because it is linked to Cloud SQL and reflected immediately when operated in MySQL Workbench.
Attempt to run a backend project through a GCP computer instance and verify that it works like local even in deployment.
Errors that have never been seen in a local environment are output.
DB access error and express.session error occur, but I don't understand why this error occurs only in the distribution version, and this error did not occur at all in the development version.
Error message output from GCP
code: 'ER_ACCESS_DENIED_ERROR',
errno: 1045,
sqlState: '28000',
sqlMessage: "Access denied for user 'root'#'localhost' (using password: NO)",
sql: undefined
}
express-session deprecated req.secret; provide secret option at app.js:34:9
Warning: connect.session() MemoryStore is not
designed for a production environment, as it will leak
memory, and will not scale past a single process.
Warning: connect.session() MemoryStore is not
designed for a production environment, as it will leak
memory, and will not scale past a single process.
const express = require('express');
const path = require('path');
const cookieParser = require('cookie-parser');
const session = require('express-session');
const dotenv = require('dotenv');
const cors = require('cors')
dotenv.config();
const AlphaRoute = require('./Routes/Alpha')
const AuthRouter = require('./Routes/Auth')
const {sequelize} = require('./models')
const app = express();
app.set('PORT', process.env.PORT || 8095);
sequelize.sync({force: false})
.then(() => {console.log('db ok')})
.catch((err) => {console.error(err)})
app.use(cors({
origin: '*'
}));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser(process.env.COOKIE_SECRET));
app.use(session({
resave: false,
saveUninitialized: false,
secret: process.env.COOKIE_SECRET,
cookie: {
httpOnly: true,
secure: false,
},
}));
app.use('/auth', AuthRouter)
app.use('/alpha', AlphaRoute)
app.listen(app.get("PORT"), () => {
console.log(app.get("PORT"), 'Online')
})
The way I tried it.
In the case of DB access error, the password input part of config.js was set to process.env.DB_PASSWORD, but as the problem continued, we changed it to input the password string.
Is it a problem that occurs because the password of the DB is not unified? I unified it with the Root password when CloudSQL was created.
What code should I modify to solve this problem?

Related

Node, Postgres and Knex: SASL: SCRAM_SERVER_FIRST_MESSAGE: client password must be a string

I am creating a site using Node.js, React, Vite, Knex.js and PostgreSQL and have run into an error when trying to start up my server and connect to my database which I don't know how to solve. I have looked around elsewhere online, which also hasn't been much help. Here are what the relevant files look like:
server.js
const express = require('express');
const PATH = 5000;
const app = express();
const cors = require('cors');
const session = require('./db/session');
const { passport } = require('./passport');
app.use(cors({
origin: process.env.VITE_CORS_ORIGIN,
credentials: true
}));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(session);
app.use(passport.initialize());
app.use(passport.session());
app.use(require('./routes'));
app.use(function(req, res, next) {
res.status(404).send("Unable to find requested resource.")
});
app.use((err, req, res, next) => {
if (err) {
req.logout();
next();
}
res.status(err.status || 500).send(err.message);
});
app.listen(PORT, () => {
console.log(`Listening on port ${PORT}`)
});
knexfile.js
const path = require('path');
require('dotenv').config({ path: path.join(__dirname, '.env.development') });
const dbMode =
process.env.VITE_ENV === 'development' ? {
client: "pg",
connection: {
host: 'localhost',
port: 5432,
user: process.env.VITE_DB_USER,
password: process.env.VITE_DB_PASS,
database: process.env.VITE_DB_NAME,
charset: 'utf8'
},
migrations: {
directory: './server/db/migrations',
tableName: "knex_migrations"
},
seeds: {
directory: './server/db/seeds'
},
} : {
client: "pg",
connection: process.env.DATABASE_URL,
ssl: { require: true }
}
module.exports = dbMode;
db.js
const knex = require('knex');
const dbConfig = require('../../knexfile');
const db = knex(dbConfig);
module.exports = db;
I also have a session store set up using express-session and connect-pg-simple. I also use Passport.js.
Whenever I try start the server ('node initServer.js') I get the error message:
<project path>/node_modules/pg/lib/sasl.js:24
throw new Error('SASL: SCRAM-SERVER-FIRST-MESSAGE: client password must be a string'
I have made sure that all my environment variables are working and are the right type. I have used console.log() to confirm that the variables aren't undefined and used typeof to confirm that the type of the environment variable for the DB password is a string.
I am using the same password details and postgreSQL installation as I used for another recent project, so I am sure that the password is not wrong and that all the details are correct.
I have no idea what I need to do to fix this as the password is (as far as I can tell) being passed correctly. I'd really appreciate your help.
If there's anything you'd like me to show or explain to help you solve this, please let me know.
Couple days I had this same issue running the knex migrate:list cli command. Checking your example, you are doing as the same way I had before. So testing changes and reading the knex documentation I reached to conclusion that:
Its seems like the knex-cli not reading the values from the
environment variables
If you still have the issue, these are the steps I follow to fixed it
1. What I did to be sure that the knex-cli was reading the environments variables, I add the client config as static string values. This will validate the reading variable issue.
2. Instead of using path lib and __dirname, I just used the relative path.
3. I tested the fix, running the knex migrate:list, and its work for me.
Maybe you others alternative of solution, but if you reach to fix the issue in ad different way please share it as a comment, so we can exchange knowledge and see a different way how to solve this issue.
Regards
I found a solution, though I am not entirely sure why this was necessary given that in projects I have done in the past this step was not required in order to connect to my database.
It turns out that the issue was connected to my session.js file. By adding the database connection object from my knexfile to express-session as follows, I was able to get around this error:
const path = require('path');
const connection = require('../../knexfile');
require('dotenv').config({ path: path.join(__dirname, '..', '..', '.env.development') });
const express_session = require('express-session');
const pgSession = require('connect-pg-simple')(express_session);
const theSecret = process.env.VITE_SESSION_SECRET;
const session = express_session({
store: new pgSession({ tableName: 'sessions', conObject: connection }),
secret: theSecret,
resave: false,
saveUninitialized: false,
cookie: { maxAge: 1000 * 60 * 60 * 24 }
})
module.exports = session;
Admittedly, I actually still have another issue despite doing this. Though I can now actually run my server, for some reason I also get the below message:
Failed to prune sessions: con.connect is not a function
I will make a separate question about this.

app connects to mongo locally, but it seems that session creation fails when deployed on heroku

So, this is my first app deployment ever and I have the following problem.
I can use mongo (via atlas) when hosting locally no problem. Users are created on sign up and they have a session. When the code is deployed to heroku (via github connection) :
when the user goes to login The login process times out. The console error/heroku logs below are displayed
when the user goes to sign up The signup process times out, but the user is created in the db.
So, how can I get the sessions to be written to mongo db when its delopyed to heroku? My instinct is that I have the environment config incorrect, but I have no idea where to go from there...
console error :
login:1 POST https://my-berlin-map.herokuapp.com/login 503 (Service Unavailable)
heroku details
2020-11-25T17:07:49.775428+00:00 heroku[router]: at=error code=H15 desc="Idle connection" method=POST path="/login" host=my-berlin-map.herokuapp.com request_id=f8671320-de7a-42c1-890d-4bdadca078a8 fwd="46.94.150.220" dyno=web.1 connect=1ms service=55124ms status=503 bytes= protocol=https 2020-11-25T17:07:49.775925+00:00 app[web.1]: [0mPOST /login [36m302[0m 119.004 ms - 46[0m
In the .env file we've specified the following (and added them as vars to heroku)
PORT=3000
ENV=development
MAPBOXSECRET= key
SESSION_SECRET=hunter2
MONGODB_URI=mongodb+srv://username:password#cluster0.lmmct.mongodb.net/dbUser?retryWrites=true&w=majority
code details
require('dotenv').config();
const bodyParser = require('body-parser');
const cookieParser = require('cookie-parser');
const express = require('express');
const favicon = require('serve-favicon');
const hbs = require('hbs');
const mongoose = require('mongoose');
const logger = require('morgan');
const path = require('path');
mongoose
.connect(process.env.MONGODB_URI, {
useCreateIndex: true,
useNewUrlParser: true,
useUnifiedTopology: true
})
.then(x => {
console.log(`Connected to Mongo! Database name: "${x.connections[0].name}"`)
})
.catch(err => {
console.error('Error connecting to mongo', err)
});
const app_name = require('./package.json').name;
const debug = require('debug')(`${app_name}:${path.basename(__filename).split('.')[0]}`);
const app = express();
// Middleware Setup
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
// session configuration
const session = require('express-session');
const MongoStore = require('connect-mongo')(session);
app.use(
session({
secret: process.env.SESSION_SECRET,
cookie: { maxAge: 24 * 60 * 60 * 1000 },
saveUninitialized: false,
resave: true,
store: new MongoStore({
mongooseConnection: mongoose.connection,
ttl: 24 * 60 * 60 * 1000
})
})
)
okay, so beyond some needed clean up of promises, the core issue was that when I entered the variable for MONGO_URI in heroku, I added an extra space at the end.
that was it.

What test can I run to ensure my Express server, session values are being stored in Redis?

Question: How can I confirm that the session values from my simple node.js test app are being stored in Redis?
Backstory: I am hosting this app on a Digital Ocean server running Centos. Redis is installed on the server and I confirmed that it was turned on using the ping command. With the app running I checked the / endpoint in Chrome and the route fired as expected. Everything seems fine but how do I check to see if Redis is actually storing the session values?
$ redis-cli ping // PONG
app.js
const express = require('express');
const session = require('express-session');
const redis = require('redis');
const RedisStore = require('connect-redis')(session);
const app = express();
const redisClient = redis.createClient();
const PORT = 8080;
app.set('view engine', 'ejs');
app.use(session({
name: 'randomWord',
resave: false,
saveUninitialized: false,
secret: 'superSecretKey',
store: new RedisStore({ client: redisClient }),
cookie: {
sameSite: true,
secure: false,
}
}));
app.get('/', function(req, res, next) {
req.session.counter += 1;
res.render('index', { output: req.session.counter });
});
app.listen(PORT, () => console.log( `app listening on port ${PORT}` ));
from index.ejs
<h1><%= output %></h1>
There's multiple ways to verify it, but probably the simpler is to connect to your Redis instance and have a look at the commands going through. An example:
$ redis-cli
$ monitor
With the monitor command, Redis will print every single command reaching the server. With your Express server up and running, try logging in, or whatever action triggers a session creation in your system. Watch the logs printed by the monitor command, and you'll easily be able to tell whether the sessions are being stored in Redis.
For further validation, stop the Express instance and run it again; you should still have the session available, as it's not stored in memory, but instead using Redis.

Error: req.flash() requires sessions

I'm new to node and I'm pretty sure I've set up the middle ware and express to use flash messaging however I still get the error:
Error: req.flash() requires sessions
Setup
//express.js
var flash = require('connect-flash')
module.exports = function (app, config, passport) {
app.use(flash());
};
//route js
exports.loginGet = function (req, res) {
res.render('users/login', {
title: 'Login',
message: req.flash('error') //error in question
});
};
What else can I do to make sure I have everything set up correctly and get it working?
From the readme (emphasis mine):
Flash messages are stored in the session. First, setup sessions as usual by enabling cookieParser and session middleware. Then, use flash middleware provided by connect-flash.
Using express-sessions with express 4, cookieParser is no longer required.
var session = require('express-session');
//...
app.use(session({ cookie: { maxAge: 60000 },
secret: 'woot',
resave: false,
saveUninitialized: false}));
In my case the issue was that Redis was not listening. I found that out by enabling the logErrors property:
new RedisStore({
host: 'localhost',
port: '6379',
logErrors: true,
});
Which resulted in messages like these:
Warning: connect-redis reported a client error: Error: Redis connection to localhost:6379 failed - connect ECONNREFUSED 127.0.0.1:6379
Please check mongodb connections. there may be an mongo error like "mongoError: Topology was destroyed".
To fix this issue, check here
i was having these issues and I solve them by respecting the cascading
app.use(passport.initialize());
app.use(passport.session());
//SESSION FLASH
app.use(flash());

Best Session Storage Middleware for Express + PostgreSQL

I'm looking for sessionstore for production app because I have error message:
Warning: connect.session() MemoryStore is not designed for a
production environment, as it will leak memory, and will not scale
past a single process
My code:
var express = require('express');
var ECT = require('ect');
var cookieParser = require('cookie-parser');
var compress = require('compression');
var session = require('express-session');
var bodyParser = require('body-parser');
var _ = require('lodash');
var passport = require('passport');
var expressValidator = require('express-validator');
var connectAssets = require('connect-assets');
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(expressValidator());
app.use(methodOverride());
app.use(cookieParser());
app.use(session({
resave: true,
saveUninitialized: true,
secret: secrets.sessionSecret,
}));
app.use(passport.initialize());
app.use(passport.session());
var app = module.exports = express();
var server = require('http').Server(app);
var io = require('socket.io')(server);
server.listen(app.get('port'), function() {
console.log('Express server listening on port %d in %s mode', app.get('port'), app.get('env'));
});
module.exports = app;
I don't know what is the best solution for my production app. I'm using sequelize as ORM with PostgreSQL.
I will be very grateful with any opinion.
Though an answer has been accepted, I think a more elaborated answer is in order, so that people who actually want to use Express with PostgreSQL for consistent session storage can have a proper reference.
Express has the session module to handle the sessions though it defaults to in-memory storage that is suitable for development stages but not for production
Warning The default server-side session storage, 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.
So for PostgreSQL there is a dedicated simple connector called connect pg simple
Once you import the connect-pg-simple you . need to pass it the session import like this:
const session = require('express-session')
const pgSession = require('connect-pg-simple')(session)
When you add the session as middleware you'll have to pass it its settings
app.use(session(sessionConfig))
and in your sessionConfig, this would be where you set all your session parameters you need to add a store option that would look like this (adding the full set of options though for the matter at hand just note the store option):
const sessionConfig = {
store: new pgSession({
pool: sessionDBaccess,
tableName: 'session'
}),
name: 'SID',
secret: randomString.generate({
length: 14,
charset: 'alphanumeric'
}),
resave: false,
saveUninitialized: true,
cookie: {
maxAge: 1000 * 60 * 60 * 24 * 7,
aameSite: true,
secure: false // ENABLE ONLY ON HTTPS
}}
The new instance of pgSesision takes two options, the pool which is the config setup to access the PostgreSQL DB and the table name.
The DB connection setting should look like this:
const sessionDBaccess = new sessionPool({
user: DB_USER,
password: DB_PASS,
host: DB_HOST,
port: DB_PORT,
database: DB_NAME})
Also note that the session pool must be initiated:
const sessionPool = require('pg').Pool
I got it working with connect-pg-simple.
Create a session table with the provided table.sql, and pass in a connection string or an object.
If you have just to store session you can use redis or maybe mongoDB if you want persistence.

Resources