I have an application with express-session#1.17.1, passport#0.3.2, and connect-redis#4.0.0. We manage the session of a user with cookies. There is a scenario when we need to let the user go to a different domain to complete the checkout process and come back. If we keep the cookie property to be strict the user's session is lost as it is unable to find the cookie, coming from 3rd party. A new session will be generated for the given user. There can be one way to handle this situation, which I can think of.
Here is my session configuration
return session({
genid: () => uuidv4(),
store: redisStore,
cookie: {
maxAge: 30 * 24 * 60 * 60 * 1000 /* 30 days */,
secure: true,
sameSite: true,
path: '/',
},
secure: true,
sameSite: true,
secret: '123',
resave: false,
name: 'prod',
saveUninitialized: true,
})(req, res, next);
Notice, I want to keep the same site property true, to prevent any CSRF. This also prevents cookies from valid third-party domains redirecting to our site.
Reattach the existing session with the session to a user coming back to the callback URL. This will require passing a session identifying token in the callback URL.
If I just pass the session-id in the callback URL, I can see the last session of the user and able to attach the. I don't think passing session-id is safe enough. I would like to know the correct way to do this.
Related
New sessions are created for every page, I'm setting the token in the server-side session after the login. But it is not available on the next page a new session is created on that page How to avoid this? The database disconnects after the API call on the page. Every time the database connection is made before calling the API. I require the token throughout the login.
src/index.ts
...
const session = require("express-session")
app.use(session({
name : 'Server_ID',
secret : 'something',
resave:false,
saveUninitialized: true,
rolling: false,
cookie: {
same site: true,
// secure: "development",
httpOnly: true,
secure: false,
maxAge:1000 * 60 * 10
}
}));
...
you must set credentials flag with your API call
for example if you're using axios, you do this:
axios.defaults.withCredentials = true;
with this approach cookie is going to sent along with your request call
make sure you have correct CORS settings too.
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'm using NodeJS + express + express-session to persist a userID from anywhere in the application.
On the first route, my session is defined
userProfileRoutes.route('/authentication').post((req, res) => {
req.session.userID = 10; //example
console.log(req.session)
}
The result of the console.log is:
Session {
cookie:
{ path: '/',
_expires: null,
originalMaxAge: null,
httpOnly: true,
secure: true },
userID: 10 } // this is the right value
But then, from a different route, I can't see the value:
userProfileRoutes.route('/edit').get(function (req, res) {
console.log('After the nav edit route');
console.log(req.session);
}
And this prints
Session {
cookie:
{ path: '/',
_expires: null,
originalMaxAge: null,
httpOnly: true,
secure: true }
} // ID VARIABLE DISAPEARS HERE
I am configuring express-session using these parameters:
app.use(session({
secret: 'secret',
proxy: true,
resave: false,
saveUninitialized: true,
withCredentials: true,
cookie: { secure: true },
store: new MongoStore({ mongooseConnection: db })
}));
Why is my userID not persisted between requests and on all routes?
You are setting cookie: {secure: true} but trying to access your server using HTTP.
From the express-session documentation:
cookie.secure
Note be careful when setting this to true, as compliant clients will not send the cookie back to the server in the future if the browser does not have an HTTPS connection.
Please note that secure: true is a recommended option. However, it requires an https-enabled website, i.e., HTTPS is necessary for secure cookies. If secure is set, and you access your site over HTTP, the cookie will not be set.
Make sure you are either using HTTPS (always in production!) or you set cookie.secure to false (maybe, and for development only!)
The secure flag in cookies
The secure flag is an option that can be set by the application server when sending a new cookie to the user within an HTTP Response. The purpose of the secure flag is to prevent cookies from being observed by unauthorized parties due to the transmission of a the cookie in clear text.
To accomplish this goal, browsers which support the secure flag will only send cookies with the secure flag when the request is going to a HTTPS page. Said in another way, the browser will not send a cookie with the secure flag set over an unencrypted HTTP request.
By setting the secure flag, the browser will prevent the transmission of a cookie over an unencrypted channel.
from https://www.owasp.org/index.php/SecureFlag
Cookies in express-session
Following common practice, express-session uses cookies to store a session ID and server side storage (mongoDB in your case) to store session data. If the browser does not send your session ID because it can't find a valid cookie, your server will assume there is no session, and save the user id on a new session on every request.
When you got to /authentication it will save the ID on a new session. When you try to read in in a different request, the session ID has changed and you have no value in userID.
So I'm using express-session with a mongo store like so:
app.use(session({
secret: 'some secret here',
saveUninitialized: false,
resave: false,
store: new MongoStore({
url: 'http://someurlhere'
})
}));
I have some login middleware, which after a successful login I want to then set the session cookie expiry time.
So I am testing with a 10 second expiry time right now using
req.session.cookie.expires = new Date(Date.now() + 10000);
I want the session expiry to reset for each subsequent request. Currently after 10 seconds have elapsed, no matter how many requests I have made after logging in, the session expires.
I feel like I have misunderstood something here!
EDIT
Ok so I missed the rolling config option in the docs, but even when I set this to true in my session config options, the same behaviour occurs:
app.use(session({
secret: 'some secret here',
saveUninitialized: false,
resave: false,
store: new MongoStore({
url: 'http://someurlhere'
}),
rolling: true,
cookie: {
maxAge: 10000
}
}));
I am now console logging the value of the cookie maxAge across my routes and see it decreasing as each subsequent request is made after logging in, it never reset back to 10000.
What am I doing wrong?
SOLVED
Ok so I came across a comment on this issue
I changed resave to true and it works as expected now.
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.