Value appears saved to Express Session but not accessible inside subsequent route - javascript

I have a problem saving data to the Express Session middleware. I am using a Vue.js frontend to communicate with the server which is running at localhost:8080. The server runs on localhost:3002.
I suspect that the interaction between the Vue app and the server may be the source of the problem as I have tried tests with a bare bones Express app that serves HTML as simple template literals and req.session.save() works fine.
Here is my code.
vue.config.js
module.exports = {
"transpileDependencies": [
"vuetify"
],
devServer: {
"https": true
}
}
index.js(Express server)
const corsOptions = {
origin: 'https://localhost:8080', // Have tried with and without these options
credentials: true,
};
app.use(cors(corsOptions));
// Set up app to use session
let sess = {
secret: 'What secret?',
resave: false, // Tried true
saveUninitialized: false, // Tried true
cookie: {secure: process.env.NODE_ENV=="prod",httpOnly: false}, // Tried true
store: MongoStore.create({ mongoUrl: process.env.DB_URL,
ttl: 14 * 24 * 60 * 60 }) // = 14 days. Default
}
app.use(session(sess));
Login route where data is being set to the session.
app.post('/api/login', async (req, res) => {
...
request(options, function (error, response) {
if (error) throw new Error(error)
let fm_res = JSON.parse(response.body)
req.session.FM_TOKEN = fm_res.response.token
req.session.save()
console.log('TOKEN STORED IN SESSION :: ', req.session) // token present in session here
res.json({message: 'Token saved in session...', status: 200})
});
});
Separate route where token is not accessible.
// CHECK AUTH
app.post('/api/token_auth', async (req, res) => {
let authToken = req.session.FM_TOKEN
console.log('TOKEN FROM SESSION : ', authToken) // undefined
....
});
I have researched and tried various solutions suggested here on SO but nothing I have found from any answers has worked. Can anyone point me in the right direction to solve this one as I am out of ideas. Thanks in advance.
UPDATE
I have tested the above endpoints with with Postman and they work, i.e. the /api/token_auth has access to the token saved on the session. So, it appears the problem might be connected to the fact that my frontend is not using https but http to make these calls.
I have tried using httpOnly: false setting in the session.cookie as suggested in this SO answer, but it still doesn't work. I am out of ideas.
Does anyone know how https requirement can be circumvented for development purposes?

Related

Redis sessions not persisting throughout different requests in express

I have 2 routes on the backend of my app.
The first one sets a session using redis and using a different route i try to retrieve the session that was saved prior. However, when trying to retrieve the session, i get a different one from the one i created.
this is how i create a session and assign a property to it:
app.post("/login", (req, res) => {
req.session.userId = 1
});
and this is how i try to retrieve it:
app.get("/cookie", (req, res) => {
res.json(req.session);
});
But when the req.session of the get request is logged, I get one without the property userID.
Even though, i am able to view the key and value of the session using the redis-client with the keys and mget command.
this is how the cookie and session is configured
app.use(
session({
name: "cookie",
store: new RedisStore({ client: redisClient }),
secret: "xxxxx",
resave: false,
saveUninitialized: false,
cookie: {
secure: false,
httpOnly: false,
maxAge: 1000 * 60 * 10,
},
})
);
and this is the redisClient:
const redisClient = redis.createClient({
host: "localhost",
port: 6379,
});
How do I properly retrieve the session that i set on the post request? Am I using redis entirely wrong or am i just missing something?
Thanks!

How to use cross-origin cookies for server-side rendering?

I have a server-side rendered web app running on localhost:3000 and the API on localhost:3010. How do I set the same cookie on both domains after a request to the API?
When I log in, I'm sending a POST request to localhost:3010 and it's setting a cookie like this:
const token = jwt.sign({ id, email }, secret, { expiresIn });
res.cookie('authorization', token, { signed: true, httpOnly: true, maxAge: 10000000 });
My problem is I can't figure out how to set that cookie on the app at localhost:3000. I was just using localStorage before, but it doesn't work for server-side rendering when I have my API and app on different domains.
Here's how my server-side rendering middleware on localhost:3000 looks like, trying to access said cookie:
import Cookies from 'universal-cookie';
export function serverSideRendering(req, res, next) {
const cookies = new Cookies(req.headers.cookie);
const token = cookies.get('authorization');
// ...
}
Try to change property name "authorization" to "JWT-token";
res.cookie('JWT-token', token, { signed: true, httpOnly: true, maxAge: 10000000 });
or you can try native node js method;
res.writeHead(200, {
'Set-Cookie': 'authorization='+token,
});
And my advice, don't use cookie, use headers fields. It's helps you in future to increase your app to ios and android apps.
And you can keep your token inside store of your app, it's more secure.

React Node Unable to pass cookie to the browser (crocs error)

For some reason I am unable to store cookie in my browser.
This is the first time I am working with React And NodeJS
My React application is working on localhost:3000 and NodeJS application on localhost:8080
The Git repository for the same happens to be this
So, I am successfully able to login, store the credentials in DB and probably serialise and de-serialise.
I am not sharing the code for Google Strategy and serialise and de-serialise since I believe that problem doesn't presist here (In case you think that you would need to view it click here
This Google redirect returns at following callback
router.get("/google/callback", passport.authenticate('google', { failureRedirect: "/", session: false }), (req, res) => {
res.redirect("http://localhost:3000/")
})
In my server.js (main file, I start node server by doing node server.js), I am doing this to store cookie
app.use(cors({
credentials: true,
origin: ['http://localhost:3000'] // here goes any Frontend IP Adress
}))
//We are setting cookie here
app.use(cookieSession({
maxAge: 24*60*60*1000, //cookies is valid for a day
keys: ['fgfhfjhfad']
}))
app.use(passport.initialize())
app.use(passport.session())
And then when I do this in my react frontend
componentWillMount() {
axios.get("http://localhost:8080/", {withCredentials: true}).then(response => {
console.log(response)
}).catch(error => {
console.log(error)
})
}
Where my localhost:3000/ looks like this
app.get("/", (req, res) => {
if (req.user) {
if (req.isFormFilled) {
res.redirect("http://localhost:3000/home")
} else {
res.json(req.user)
}
} else {
res.json("user does not exsist")
}
})
It always log res.json("user does not exsist") but if I directly go to localhost:3000 in my browser, I can see my req.user < [See: update below question]
Ps; I am enabling cross-origin request in my browser
[Question:] Can someone please help me in finding out what I could be doing wrong?
[Update:] It appears we might be having crocs error, I have changed my code and I am getting this as an error in frontend
Access to XMLHttpRequest at 'localhost:8080' from origin
'localhost:3000' has been blocked by CORS policy: The value of the
'Access-Control-Allow-Origin' header in the response must not be the
wildcard '*' when the request's credentials mode is 'include'. The
credentials mode of requests initiated by the XMLHttpRequest is
controlled by the withCredentials attribute
If I remove {withCredentials: true} in my axios request the above error disappears but then it logs user does not exsist in response.data
So send Cookies via REST its neccecary to:
Set Cors serverside:
app.use(cors({
'allowedHeaders': ['sessionId', 'Content-Type'],
'exposedHeaders': ['sessionId'],
'credentials': true,
'origin': ['http://[FRONTEND-IP]:[FRONTEND-PORT]', 'http://[ALTERNATIVE-FRONTEND-IP]:[FRONTEND-PORT]'],
}))
For Frontend you need to setup a call like this:
fetch('http://[API-IP]:[API-PORT]/call', {credentials: 'include'}).then((result) => {
return result.json()
}).then((data) => {
//Do something
});
you can also use fetch asynchronous:
async function loadData() {
let response = await fetch(
'http://[API-IP]:[APi-PORT]/call',
{credentials: 'include'}
);
return response.json();
}
this, of course, applies to using a rest service with json bodies.
If you rely on another structure than json, you need to parse your response manually.
Also, An interested article on web about cors https://50linesofco.de/post/2017-03-06-cors-a-guided-tour
If you face Problems with Passport-Sessions try to use 'express-session' instead.
'express-session' Creates Cookies itself and sends it.
app.use(session({
secret: 'keyboard cat',
resave: false,
saveUninitialized: true
}))
If you use this you can grab the session in each rest call.
app.get('/', (req, res) => {
req.session.[anyVariableUWantHere] = true;
res.send('done');
});
Now the session is created.
If you get any Problems with React try this:
in server.js:
app.use(cors({
credentials: true,
[...]
}))
now you just need to edit your 'fetch' in React like that:
fetch('http://localhost:3000/call', {credentials: 'include'}).then((result) => {
return result.json()
}).then((data) => {
//Do something
});
The credentials: 'include' is important for React to get the Session-Cookie.
By Default fetch is not loading any Cookies.

Express session variables don't persist across requests

I'm trying to get sessions variables working in my Express Node.js project, using the express-session module. I'm not getting any errors, and setting session variables seems to work, however they don't persist across requests. Here's a simplified bit of my code:
server.js
var express = require('express');
var bodyParser = require('body-parser');
var session = require('express-session');
var app = express();
app.use(bodyParser.json());
// Sessions
app.use(session({
secret: config.secret
}));
app.use('/api/projects', require('./controllers/api/projects'));
var server = app.listen(3000, function() {
console.log('Server listening on', 3000)
});
api/projects.js router
var router = require('express').Router()
router.get('/set', function(req, res, next) {
req.session.test = "test";
res.status(200).json({"test":req.session.test});
});
router.get('/current', function(req, res, next) {
res.status(200).json({"test":req.session.test});
})
Setting the variable seems to works...
The /set API call should set a session variable called test. This API call appears to work when I try it with curl:
curl -X GET localhost:3000/api/projects/set --cookie "connect.sid=s%3AyP3a8siRuA-5jDxWH4T03UxNpFd6lfBq.Ha8b8eJxbtW8fAJlbgR9jumfmBpJIXNE6444fOb2Jro"
{"test":"test"}
This is also confirmed in the console log:
Session {
cookie:
{ path: '/',
_expires: null,
originalMaxAge: null,
httpOnly: true },
test: 'test' }
...however next time I check it it's not set
When I try to check the variable with my second API call, it appears to be undeclared:
curl -X GET localhost:3000/api/projects/current --cookie "connect.sid=s%3AyP3a8siRuA-5jDxWH4T03UxNpFd6lfBq.Ha8b8eJxbtW8fAJlbgR9jumfmBpJIXNE6444fOb2Jro"
{}
This is confirmed in the console log, the test variable is no longer set:
Session {
cookie:
{ path: '/',
_expires: null,
originalMaxAge: null,
httpOnly: true } }
How can I ensure my sessions variables persist across requests?
(PS: I've been stuck on this for a while and any small comment on hints or resolution is very welcome, even if you're not sure you've got the answer)
I can see two possible reasons for this.
First, you could've messed up with cookies in curl.
You could ensure that you've set your cookies correctly by checking http response. If it contains Set-Cookie header with a new connect.sid then you did something wrong.
Alternatively, you could use a web browser with native cookies support to guard yourself against such mistakes.
Second, you may've restarted your sever between two requests.
Since you didn't specify any persistent store for your sessions, no data will persist between node.js server restarts.
If you want session data to persist after node.js server stops or restarts, you should consider using some persistent session store (i.e. redis-store).

Express session not persisting

I'm trying to set up a basic session system in node. Here's what I've got so far:
app.js:
app.use(express.cookieParser('stackoverflow'));
app.use(express.session());
I'm setting the session data in ajax.js:
addClassToCart: function(req, res) {
req.session.cart = req.body.classId;
console.log(req.session.cart);
}
This logs the correct information. However, when I try to retrieve that information elsewhere (same file, different function):
console.log(req.session.cart);
I get undefined. I feel like I'm missing something incredibly basic. Various tutorials for this are either awful or require me to add in even more packages (something I'm trying to avoid).
More data from my debugging:
This works with non-AJAX requests
The session is set before it's logged.
As it turns out, the issue wasn't with Express' session (as the other answers seem to think). Rather, it was a misunderstanding on my part. I changed addClassToCart to the following:
addClassToCart: function(req, res) {
req.session.cart = req.body.classId;
console.log(req.session.cart);
res.send('class added');
}
Adding res.send() fixed the problem.
As noted in the answer to a related SO question, this can also occur if you're using fetch to get data from your server but you don't pass in the credentials option:
fetch('/addclasstocart', {
method: 'POST',
credentials: 'same-origin' // <- this is mandatory to deal with cookies
})
Cookies won't be passed to the server unless you include this option which means the request's session object will be reset with each new call.
I don't know about basic session store, but redis only took me a few minutes to setup:
app.use(express.session({
store: new RedisStore({
host: cfg.redis.host,
db: cfg.redis.db
}),
secret: 'poopy pants'
}));
On a mac:
brew install redis
app.use(express.session({
secret: "my secret",
store: new RedisStore,
cookie: { secure: false, maxAge:86400000 }
}));
Not sure the problem is in session age, but it just to be safe, I'd suggest you to specify maxAge.

Categories