I have a NextJS client running on localhost:3001 and a Express/MongoDB server running on localhost:3000.
For authentication I'm using express-session with connect-mongo like so:
app.use(session({
secret: 'jordan-peterson-is-a-fraud',
resave: false,
saveUninitialized: false,
unset: 'destroy',
cookie: {
httpOnly: false
},
store: new MongoStore({ mongooseConnection: mongoose.connection })
}));
On login I set req.session.user = userID, which seems to work fine: it registers a new session record in the sessions table in my database, and sends a set-cookie header with the value connect.sid=<encrypted-session-ID> to the client which gets stored in a session cookie.
So far, so good.
But on logout it seems that calling req.session.destroy() has no effect whatsoever. The client sends a POST with credentials to /logout on the server:
fetch('http://localhost:3000/logout', {
method: 'post',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
mode: 'cors',
credentials: 'include'
})
And the server does seem to receive a correct req object that includes:
{
...
sessionID: '<encrypted-session-ID>',
session: Session {
cookie: {
path: '/',
_expires: null,
originalMaxAge: null,
httpOnly: false
}
}
...
}
The console also prints [Function: destroy] when I log req.session.destroy. But nothing happens when I call it. The database is unchanged - with the session record still there from the login.
router.all('/logout', async function(req, res){
if (req.session) {
req.session.destroy();
return res.end();
}
}
Anyone know what I'm doing wrong here?
You can use delete req.session.user;
req.session.destroy(req.sessionID)
Related
I've been working on this problem for a while, and I am stumped.
I am using express-session with connect-redis as a store for the sessions. I am using typescript.
I added this to the top of my server file and I am able to access the fields without any typescript errors.
declare module "express-session" {
interface SessionData {
userId: string;
role: UserRole;
}
}
When I log a user in, I set the extra SessionData fields.
request.session.userId = user._id.toHexString();
request.session.role = user.role;
console.log(request.session);
Via Postman, I can see that the cookie is set and returned. Additionally, I checked Redis for the session key and the session is correct. e.g.
{\"cookie\":{\"originalMaxAge\":604800000,\"expires\":\"2023-01-23T17:34:05.158Z\",\"secure\":true,\"httpOnly\":true,\"path\":\"/\"},\"userId\":\"1234\",\"role\":\"userRole\"}
However, the extra fields I added to SessionData are not being populated after calling the session middleware. Instead of getting:
Session {
cookie: {
path: '/',
_expires: 2023-01-23T17:52:49.153Z,
originalMaxAge: 604800000,
httpOnly: true,
secure: true
},
userId: '1234',
role: 'userRole'
}
I get:
Session {
cookie: {
path: '/',
_expires: 2023-01-23T17:52:52.339Z,
originalMaxAge: 604800000,
httpOnly: true,
secure: true
}
}
This is how I am calling the session middleware:
const sessionOptions: SessionOptions = {
secret: "secret key",
resave: true,
saveUninitialized: true,
cookie: { secure: true, httpOnly: true },
store: store,
};
app.use(session(sessionOptions));
I thought it may have been an issue with redis, but the key/value is being persisted. I thought maybe it was an issue with connect-redis, so I used the default MemoryStore, but that doesn't work either.
Any help would be appreciated!
Of course right after I ask this question I figure out what the answer is...
First, don't manually set the cookie for the session id in the response. It will automatically be done for you.
Second, ensure that secure is FALSE on localhost.
I tried to see if my cookies is working ,so here's my code
const RedisStore = connectRedis(session)
const redisClient = redis.createClient()
app.use(
session({
//name: 'qid',
store: new RedisStore({ //ttl: how long it should last
client: redisClient,
//disableTTL :true, //make sure session last forever
//disableTouch: true, // make sure it does'nt have to update the last time it's ttl
}),
cookie:{
maxAge: 1000*60*60*24*365*10, //10 years
path: "/"
//httpOnly:true, //javascript front end can't access
//sameSite:'none', // csrf
//secure:false
//secure: __prod__ //cookie only works in https
},
saveUninitialized:true, //automatically create a empty session on default
secret: 'some secret', //env
resave: false,
})
)
app.listen(4000,()=>{
console.log('server stared on localhost:4000')
})
app.get('/products', (req,res,next) => {
console.log(req.session);
if(!req.session.userId){
req.session.userId = 1
}else{
req.session.userId = req.session.userId +1
}
console.log(req.session.userId) //test if work
res.send("hello")
})
So here's the thing, when I connect to localhost:4000/products, In the cookie session, I can only see these
But when I print out the results on vscode console, I can see the number is growing like below , so I do have a session, it's just not showing on the browser , can anyone tell me why is that?
server stared on localhost:4000
Session {
cookie: {
path: '/',
_expires: 2031-08-18T12:59:30.827Z,
originalMaxAge: 315360000000,
httpOnly: true
},
userId: 10
}
11
Session {
cookie: {
path: '/',
_expires: 2031-08-18T13:00:37.257Z,
originalMaxAge: 315360000000,
httpOnly: true
},
userId: 11
}
12
So I got a solution after a lot of tests, So if you only set your cookie to same-site:"none" without secure options ,it would be like my situation,but if you want to turn on secure option your endpoint have to be https, so I don't think this was the answer, and you can change to lax or other options it would act normal in your localhost,
Work in local host
lax
(don't set same site)
But due to secure policy https://www.chromium.org/updates/same-site you can't not pass cookie to some certain website (in my case I want to test cookies in my graphql apollo studio) without setting same-site:"none" secure, so I use mkcert to use https in my localhost
https://web.dev/how-to-use-local-https/ , and everything works,
Work
samesite : none
secure : true
https:yourendpoint
I am not able to view req.session saved data after storing userId after user logs in.
When I console.log the session from the login function I get the proper data...
Session {
cookie:
{ path: '/',
_expires: 2019-02-23T12:17:24.134Z,
originalMaxAge: 7200000,
httpOnly: true,
sameSite: true,
secure: false },
userId: 4 }
But when I console.log(req.session) from other routes after that I get a new blank session
Session {
cookie:
{ path: '/',
_expires: 2019-02-23T12:12:47.282Z,
originalMaxAge: 7200000,
httpOnly: true,
sameSite: false,
secure: false } }
I am working on my localhost using React frontend on port 3000 and Node/Express with express-session and redis-connect. When I view my Redis I see the stored session properly.
This is my session code...
app.use(
session({
store,
name: 'sid',
saveUninitialized: false,
resave: false,
secret: 'secret',
cookie: {
maxAge: 1000 * 60 * 60 * 2,
sameSite: true,
secure: false
}
})
)
I have tried all different values for these options and nothing works. Please help!
The answer came from a mix of answers from RobertB4 https://github.com/expressjs/session/issues/374
Apparently this problem is common when using CORS middleware and fetch api. In all of your fetch calls you need to send a credentials property set to 'include' and in your cors middleware you need to set an option to credentials: true. It is not good enough to do one or the other, you must do both...
fetch('url', {
credentials: 'include'
})
const corsOptions = {
credentials: true
}
I have built Node.js app with Express 4, for manage sessions I use connect-mongo middleware, all works.
But I need login to my app from another site.
App is hosted on aws EC2.
I use SalesForce and after login to it, I want open my app, but DON'T want input credentials...
On node.js server I have added headers:
res.setHeader('Access-Control-Allow-Origin', req.headers.origin);
res.setHeader('Access-Control-Allow-Credentials', 'true');
In SF, onClick button I execute:
jsonData = {
"email": 'test1#example.com',
"password": "test"
}
$.ajaxSetup({
type: "POST",
data: {},
dataType: 'json',
xhrFields: {
withCredentials: true
},
crossDomain: true
});
$.post( 'http://ec2-someip.us-west-2.compute.amazonaws.com//login', jsonData)
.done(function( data ) {
console.log( "done" );
console.log(data);
//redirect to data url
})
.fail(function(data) {
console.log( "error" );
console.log( "data" );
});
Node.js returns me correct data url, but doesn't add session cookie, and that's why I see login page after redirect...
When I manually send POST request from browser (I use "Rest Console" app for Google Chrome), node.js added cookie.
What is wrong?
There is a way to login from SF (or any other site) ?
Thank you.
Fixed by adding cookie domain settings:
app.use(session({
secret: config.get('session:key'),
saveUninitialized: true,
resave: true,
store: new MongoStore({
db: mongoose.connection.db
}),
cookie: {
path: '/',
domain: utils.isDevelopmentEnv() ? null : '.' + config.get('domain').replace('http://', '').replace('https://', ''),
httpOnly: true
}
}));
I am using passport-twitter to set up a twitter connect on my site. Users can connect by clicking on 'login' or on 'add new item'. The only difference between the 2 is that if they click on add new item, a modal window is supposed to open once theyre logged in.
To know on button they click, I store the url in req.session.referrer:
// route for twitter authentication and login
app.get('/auth/twitter', function(req, res, next){
req.session.referrer = req.url;
console.log(req.session);
passport.authenticate('twitter')(req, res, next);
});
app.get('/auth/twitter/new', function(req, res, next){
req.session.referrer = req.url;
console.log(req.session);
passport.authenticate('twitter')(req, res, next);
});
// handle the callback after twitter has authenticated the user
app.get('/auth/twitter/callback', function(req, res, next){
var options = {
successRedirect : '/twitter-user/signin',
failureRedirect : '/'
};
console.log(req.session);
if (req.session.referrer && req.session.referrer.indexOf('new') > -1) options.successRedirect = '/twitter-user/new';
passport.authenticate('twitter', options)(req, res, next)
});
Everything works fine in my development environment but once online I get this error message:
Express
500 Error: Failed to find request token in session
at Strategy.OAuthStrategy.authenticate (/app/node_modules/passport-twitter/node_modules/passport-oauth1/lib/strategy.js:142:54)
...
My settings are set up properly in Twitter. Here is what I get with the logs:
For the request:
{ cookie:
{ path: '/',
_expires: null,
originalMaxAge: null,
httpOnly: true },
passport: {},
referrer: '/auth/twitter' }
For the callback:
{ cookie:
{ path: '/',
_expires: null,
originalMaxAge: null,
httpOnly: true },
passport: {} }
Maybe it could be due to subdomain problem ( http://example.com vs http://www.example.com) as I don't have the pb locally.
How can I fix this?
Many thanks
EDIT: My API key is set up like this (as per this tutorial: http://scotch.io/tutorials/javascript/easy-node-authentication-twitter):
passport.use(new TwitterStrategy({
consumerKey : configAuth.twitterAuth.consumerKey,
consumerSecret : configAuth.twitterAuth.consumerSecret,
callbackURL : configAuth.twitterAuth.callbackURL
},function(token, tokenSecret, profile, done) {
...
});
First if your callback is localhost and you are using express-session make sure the cookie secure option is set to false. e.g
// Express session
let sess = {
secret: 'secret',
resave: true,
saveUninitialized: true,
cookie: {
secure: false,
}
}
You can also change that in production by
if (process.env.NODE_ENV === 'production') {
app.set('trust proxy', 1) // trust first proxy
sess.cookie.secure = true // serve secure cookies
}
If that didn't work then check if you have set the cookie sameSite option to Strict. Change it to Lax. e.g
let sess = {
secret: 'secret',
resave: true,
saveUninitialized: true,
cookie: {
secure: false,
sameSite: 'Lax'
}
}
I got the same Error but solved it.. and here's what my issue was and the solution.
Actuality, it didn't like my redirect url "http:// localhost /auth/twitter/callback". I changed it to "http:// 127.0.0.1 /auth/twitter/callback". In my actual code, I had to keep it as localhost or I'd get errors about a missing token