Firebase node.js application opens someone else's profile - node.js

I have a node.js application which I deployed as Firebase function. The issue that I am having is, if user1 is logged it from a window and user2 open the url in a seperate device+browser, User2 is automatically logged in as user1. I am not sure why this is happening.
Some things I have tried
Setting the Auth persistence to None.
Changing my session config in index.js which looks like
app.use(session({
name: '__session',
secret: 'random',
resave: true,
saveUninitialized: true,
cookie: {
maxAge: 5 * 24 * 60 * 60 * 1000, // 5 days
secure: false,
httpOnly: true
}
}));
app.use(flash());
app.use(async(req, res, next) => {
res.locals.success = req.flash('success');
res.locals.error = req.flash('error')
res.locals.currentUser = firebase.auth().currentUser;
next();
});
Anyone faced similar issue? Any idea how to fix this?
Thanks a lot in advance!

Session data is not saved in the cookie itself, just the session ID. Session data is stored server-side.

Related

Express-session cookie not saving in browser

I've been researching this for hours now. What could be the reason why the cookies are not being saved in the browser? I'm using express-session. Below are the pieces of code I'm using.
const app = express();
// CORS config
app.use(cors({
origin: process.env.API_URL,
credentials: true,
optionsSuccessStatus: 200
}));
app.use(cookieParser());
// Where the sessions are stored
const MongoDBStore = new MongoDBSession({
uri: process.env.MEDIRECORDS_URI,
collection: "sessions"
})
app.set("trust proxy", 1);
const oneDay = 1000 * 60 * 60 * 24;
app.use(session({
name: "irmp_session",
secret: process.env.AWS_SESSION_KEY,
resave: false,
saveUninitialized: false,
maxAge: 7200000, // 2 hrs validity
store: MongoDBStore,
cookie: {
path: '/',
sameSite: false,
secure: false,
maxAge: oneDay
}
}))
When I try to login using the frontend, the login is successful, the session is stored in the database. However, when I check the cookie storage, it is empty.
After spending hours of researching, I learned that this is due to Chrome's cookie updates. Here is what the update is all about.
As the link states, for a cookie to be saved in Chrome and if it is really needed to set the sameSite to none, developers should set the secure option to be true. Default value of sameSite if not set is lax.
Hope this helps anyone who might encounter the problem.
If anyone here uses heroku or render.com for free, I added all the answers above but it is still not working. I have tried another solution here which is add app.set("trust proxy", 1); before app.use(session(sessionSettings)) and it now saves cookie to different browsers.
Thank you so much for sharing it. I was stack on this for 2 days now, in localhost things worked perfectly, but after deploy my MERN app in differents servers, cookie stoped working...
using express-session:
app.use(session({
.......
.......
cookie:{
maxAge: 24*60*60*1000, //please change it based on your needs
secure: app.get('env') === 'production'?true:false,
sameSite: 'none'
}}));
this will solve the problem!!

Unable to verify authorization state on Heroku

I'm running a nodejs/reactjs app on heroku. I implemented google login using Passport. I'm getting an error "Unable to verify authorization state" when people try to login.
I see here NodeJS Express Session isn't being restored between routes in kubernetes that I need to set the X-Forwarded-SSL header. How do I do that according to what the question says?
The solution outlined on that page also mentions Apache, but Heroku doesn't make use of Apache to forward requests to apps' web dynos.
Is anyone running into the same issue on Heroku?
So the weird thing is when I try to login, it works the second time but the first time, I get the error "Unable to verify authorization state".
here's my index.js
const session = require("express-session");
app.use (
session ({
secret: "ddd",
resave: false,
saveUninitialized: true,
cookie: {
expires: 60 * 60 * 24,
secure: (app.get('env') === 'production')
}
})
);
if (app.get('env') === 'production') {
app.set('trust proxy', 1); // trust first proxy
}
So as I mentioned your issue might not be related to the request headers because in my issue, session never persisted, whereas yours does in your second attempt. It might be an issue with your verify function or your deserializeUser function.
Here's an example. I don't use Google auth personally, but something else. My code looks similar, but I got some of the Google auth code from https://github.com/jaredhanson/passport-google/blob/master/examples/signon/app.js. Fill in your stuff where appropriate and debug/log what's coming in to see how your functions are being called.
passport.use(new GoogleStrategy({
returnURL: 'http://localhost:3000/auth/google/return', // replace with yours
realm: 'http://localhost:3000/' // replace with yours
}, function (identifier, profile, done) {
// console.log identifier and profile, or even better, use your debugger on this line and see what's happening
// Do whatever you need to do with identifier and then..
return done(null, profile);
}));
passport.serializeUser(async (user, done) => {
// console.log user or use your debugger on this line and see what's happening
const userInDb = await db.getUser(user) // Find your user in your db using based on whatever is in the user object
const serializedSessionUser = {id: userInDb.id, username: userInDb.username} // this is the object that'll appear in req.user. Customize it like you want
return done(null, serializedSessionUser); // Commit it to the session
});
passport.deserializeUser((user, done) => {
done(null, user);
});
Edit: Apparently there's 2 ways to use Google for passport. The first is OAuth / OAuth 2.0. The second is OpenID. I used OpenID in this example. Adjust accordingly!
Edit 2: Here's my own equivalent to your index.js:
app.use(session({
cookie: {
sameSite: 'lax',
secure: !['development', 'test'].includes(process.env.NODE_ENV),
maxAge: 1000 * 60 * 60 * 24 * 30, // 30 days
},
proxy: true,
resave: false,
saveUninitialized: true,
secret: process.env.COOKIE_SECRET,
store: 'your store',
rolling: true,
}));

I can't set the SameSite attribute of the cookie to None in Nodejs (Express)

We are creating a backend for a Twitter view app in Nodejs (Express).
I'm thinking of using Twitter Api for login and storing the token returned after authentication to the session and then restoring the session from the cookie when it is accessed again.
However, the cookie is blocked when it is accessed again and I can't restore the session information.
The browser I use is chrome, but since chrome version 80, SameSite attribute seems to be Lax (sends a cookie when called from the site of the same domain) when the SameSite attribute is not specified, and in this case, front and back end are different domains, so cookies are blocked.
So I am trying to set the SameSite attribute to None (sends a cookie when called by any site), but I can't seem to set it well and asked this question.
I'm wondering if I can set the SameSite attribute to None if I make a difference in the part of app.use(session({})), but...
If anyone knows of a solution, I would appreciate your help.
Thank you for your help.
The corresponding source code
callback_url = env.URL + "oauth/callback";
app.use(
cookieSession({
name: "session",
keys: ["thisappisawesome"],
maxAge: 24 * 60 * 60 * 100
})
);
app.use(cookieParser());
// Save to session
passport.serializeUser(function(user, done) {
done(null, user.id);
});
// Restore from Session
passport.deserializeUser(function(user, done) {
done(null, user);
});
passport.use(
new TwitterStrategy({
consumerKey: env.TWITTER_CONSUMER_KEY,
consumerSecret: env.TWITTER_CONSUMER_SECRET,
callbackURL: callback_url
},
async (token, tokenSecret, profile, done) => {
return done(null, profile);
}
));
app.use(session({
allowedHeaders: ['sessionId', 'Content-Type'],
exposedHeaders: ['sessionId'],
secret: 'reply-analyzer',
resave: false,
saveUninitialized: false
}));
var cors_set = {
origin: env.CORS_ORIGIN_URL,
methods: "GET,HEAD,PUT,PATCH,POST,DELETE",
credentials: true // allow session cookie from browser to pass through
};
app.use(passport.initialize());
app.use(passport.session());
app.use(cors(cors_set));
What I've tried.
1.I tried setting the cookie options in the app.use(session({})) part, but it was not possible to set the SameSite attribute to None.
app.use(session({
allowedHeaders: ['sessionId', 'Content-Type'],
exposedHeaders: ['sessionId'],
secret: 'reply-analyzer',
resave: false,
saveUninitialized: false,
cookie : {
secure: true,
sameSite: 'None'
}
}));
2.I tried using the following middleware (express-samesite-default), but the SameSite attribute can be set to None, and the It wasn't.
var sameSiteCookieMiddleware = require("express-samesite-default");
app.use(sameSiteCookieMiddleware.sameSiteCookieMiddleware());
Additional information
Node.js v12.18.2
chrome v84.0.4147.135
I was able to self-resolve and will describe how I was able to solve the problem.
In the code there are two sessions and a cookie session, but I decided to use the cookie session as it seems to work fine.
The end result is the following
var cookieSession = require("cookie-session");
app.set('trust proxy', 1)
app.use(
cookieSession({
name: "__session",
keys: ["key1"],
maxAge: 24 * 60 * 60 * 100,
secure: true,
httpOnly: true,
sameSite: 'none'
})
);
Hey I just used like this. And it worked. I'm using localhost for both frontend and express backend.
res.cookie('token', token, {
expires: new Date(Date.now() + (3600 * 1000 * 24 * 180 * 1)),
httpOnly: true,
sameSite: "none",
secure: "false",
});
try SameSite: 'none' with capital S it worked for me but i used express-session with cookie-parser... i think your code not working because of small s, when i change my to sameSite it's not working for me too, but SameSite works just as expected
also i use npm i cors
here is my piece of code
app.use(session({
key: 'session_cookie_user_auth',
secret: 'mooncore',
store: sessionStore,
resave: false,
saveUninitialized: false,
cookie: {
SameSite: 'none',
maxAge: 1000 * 60 * 60 * 60
}
}));

Renewing/Refreshing Express Session

In my app I restrict some access to some actions and pages if a user is not logged in. I have:
var restrict = function(req, res, next) {
if (!req.user) {
console.log("USER isn't logged in.")
return res.status(403).send('Access or action denied, please log in');
}
next();
}
app.get('/stocks', restrict, MainHandler.findAllStocksFromUser);
app.get('/stocks/:id', MainHandler.findStockByIdAndDates);
app.put('/stocks/:id/stockActions', restrict, MainHandler.handleStockAction);
I'm essentially trying to refresh a session everytime the client makes a request to the server so that the server doesn't logout the user/destroy the session when it shouldn't. For testing, I want the session to expire/the user to be logged out if 20 seconds go by without the user making an requests to the server. I have:
app.use(session({secret: 'secret', saveUninitialized: true, resave: true, expires: new Date(Date.now() + (20000))}));
Then I try to use middleware to refresh the expiration date every time the use makes a request:
// Session-persisted message middleware
app.use(function(req, res, next){
req.session.cookie.expires = new Date(Date.now() + 20000);
next();
});
But if I log in from the client, and click around, causing requests to the server, I still get the log-in error on the client after 20 seconds, despite trying to "refresh" the session in the middleware. I have also tried using maxAge using the same strategy with the middleware. Any ideas? Thanks!
You can try define your session as follows
app.use (
session ({
secret: "secret",
saveUninitialized: true,
resave: true,
cookie: {
expires: 20 * 1000
}
})
);
and then refresh the session using
req.session.touch()
or you could define your session as
app.use (
session ({
secret: "secret",
saveUninitialized: false,
resave: true,
rolling: true,
cookie: {
expires: 20 * 1000
}
})
);
and it will renew the session automatically and it will only expire when it has been idle for the value in the expires variable
express-session supports a duration-based maxAge setting, which will work better than setting a fixed date for all sessions. So your middleware usage should instead look like:
app.use(session({
secret: 'secret',
saveUninitialized: true,
resave: true,
maxAge: 20000
}));
Next, to update the expiration of the session, you can just call req.session.touch(); if that is all you're doing to the session and its contents.
The documentation has a lot of other good information on controlling session expiration and related topics.

Passport.js / Express.js Creating New Session on Every Network Request

I have a working login function that properly authenticates and saves. However, express never remembers the old session, it always creates a new one for every network request.
Evidently, Passport is exceedingly sensitive to the order that express middleware is initialized. (Example: https://www.airpair.com/express/posts/expressjs-and-passportjs-sessions-deep-dive). I checked my config against a number of examples and rearranged it to so if it would help, but it hasn't moved my bug. Either that isn't the issue or I just haven't found the config holy grail yet. Here's my current config:
Express Config
app.engine('html', require('ejs').renderFile);
app.set('view engine', 'html');
app.use(express.static(path.join(config.root, '/views')));
app.set('views', config.root + '/views');
var sessionOpts = {
saveUninitialized: true,
resave: false,
store: new RedisStore({
host: 'localhost',
port: 6379
}),
secret: 'keyboard',
cookie: {
httpOnly: true,
maxAge: 1000
}
}
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({
extended: false
}));
app.use(cookieParser('keyboard'));
app.use(session(sessionOpts));
app.use(passport.initialize());
app.use(passport.session());
app.use(cors());
require('./routes/router.js')(app, passport);
Passport Config
passport.use('local-login', new LocalStrategy({
usernameField: 'email',
passwordField: 'password',
passReqToCallback: true
},
function(req, username, password, done) {
client.hgetall(username, function(err, reply) {
if (err) {
return done(err);
}
if (!reply) {
return done(null, false, {
message: 'Incorrect username.'
})
}
if (reply.password !== password) {
return done(null, false, {
message: 'Incorrect password.'
})
}
return done(null, reply)
})
}));
Does Passport need handholding for Redis? Redis sessions are stored in a 'sess' folder with a key like so: sess:RhodmaK2V2wDNLglV5j1B6rC. All of the tutorials I've found have been about Mongo so I'm not sure if I need to somehow include the session key when trying to look it up. Within the session entry, it's properly stored in standard cookie form for passport though: req.session.passport.user
Is there any way to see what is happening inside of passport initialize? On subsequent requests it is supposed to do this: "The general passport middleware we setup (passport.initialize) is invoked on the request, it finds the passport.user attached to the session. If is doesn't (user is not yet authenticated) it creates it like req.passport.user = {}." See 'SUBSEQUENT AUTHENTICATED REQUESTS FLOW' - http://toon.io/understanding-passportjs-authentication-flow/ I strongly suspect my problem lies at that step, so it would be nice to be able to see inside of it.
Some interesting tidbits:
Passport has never once called deserializeUser. I assume it's never
reached that point.
I have checked the other StackOverflow questions about this problem,
but none of those solutions worked for me.
I've checked to see if the new sessions are generated by any static resources but they are not. It's only for intentional server requests. On the pages without them, no new sessions are made.
All sessions have either a populated or empty req.session.passport property.
Your session ID might be different for every request because you have your cookie set to expire after 1 second. cookie.maxAge here is in ms:
cookie: {
httpOnly: true,
maxAge: 1000
}
Edit: I also have to nag you about storing passwords in plaintext. Don't do this ;)

Resources