Search code examples
reactjsnode.jsmongodbexpresssession-variables

Session Not Stored In Express


I am creating a React app with Express as the backend and using MongoStore to store the session data. When I log in to Cloud Mongo in Atlas, I can see a session being created and my variable is stored. But when I try accessing the session, I get undefined.

I am doing this in my express start file:

const express = require('express');
const cors = require('cors');
const session = require('express-session');

const mongoose = require('mongoose');
const MongoDBStore = require('connect-mongodb-session')(session);
const MONGODB_URI = "MY MONGODB ATLAS URL";
const store = new MongoDBStore({
    uri: MONGODB_URI,
    collection: 'sessions'
});

const app = express();
const port = process.env.PORT || 3001;

app.use(cors());
app.use(session({
    secret: 'mysecretkey',
    resave: false,
    saveUninitialized: false,
    store: store
}));
app.use(express.json());

app.get('/csrf', (req, res) => {
    if(req.headers['sec-fetch-site'] === 'same-site'){
        const csrf = 'ABCDEFG';
        req.session.csrf = csrf;
        req.session.save(err => {
            if (err) {
                console.log('Session save error:', err);
                return res.status(500).json({ error: 'Session save error' });
            }
            console.log('CSRF Token set:', csrf);
            res.json({ csrf: csrf });
        });
    }else{
        res.status(403).json({ error: 'Invaid Entry' })
    }
});

app.get('/test-get', (req, res) => {
    res.json({ message: req.session })
})

app.post('/login', (req, res) => {
    if(req.headers['sec-fetch-site'] === 'same-site'){
        console.log('Session data on /login:', req.session);
        console.log('CSRF Token on /login:', req.session.csrf);
        if (req.session.csrf) {
            res.json({ message: 'Login successful', csrf: req.session.csrf });
        } else {
            res.status(400).json({ error: 'CSRF token missing' });
        }
    }else{
        res.status(403).json({ error: 'Invaid Entry' })
    }
})

mongoose
    .connect(
        MONGODB_URI
    )
    .then(result => {
        app.listen(port, () => {
            console.log(`Server running on port ${port}`);
        });
    })
    .catch(err => {
        console.log(err);
});

In React I am fetching like this:

To Set Token:

const[csrf, setCSRF] = useState('');

    useEffect(() => {
        fetch(PROCESSURL + 'csrf', { method: 'GET' })
            .then((res) => res.json())
            .then((data) => {
                setCSRF(data.csrf);
            })
            .catch(() => {
                alert('Server: 403');
            })
    }, []);

To Post Login:

const handleSubmit = (event) => {
        event.preventDefault();
        formData['csrf'] = csrf;
        fetch(PROCESSURL + 'login', { 
            method: "POST",
            headers: {
                'Content-Type': 'application/json',
                csrf: csrf
            },
            body: JSON.stringify(formData)
        })
            .then((res) => res.json())
            .then((data) => {
                alert(data);
            })
    }

Please help me. I am new to Express.


Solution

  • As PROCESSURL isn't specified, I can't be sure if it's on the same origin or not, but considering the existence of the CORS middleware, I'm assuming it's not.

    By default, fetch doesn't send cookies and other authentication data unless the request goes to the same origin. You can choose to include credentials when making a new fetch request: fetch(url, { credentials: 'include' }).

    You will need to send the Access-Control-Allow-Credentials header, which the cors package you've included can do:

    app.use(cors({
      origin: true,
      credentials: true,
    }))
    

    Both the default "*" and the above true value for for the Origin CORS header are useful during development but make sure to lock them down to only allowed origins during production: ["https://example.com", /\.example\.org$/ ], for example.