16

I have a nodejs/express + React CRA application, and I'm trying to set a cookie from nodejs. The server is on port 4001, so in my React application's project.json I have "proxy": "http://localhost:4001" set, but the cookie still won't get set in the browser.

I've tested it in production mode as well, with the React application being served from nodejs directly and it all works fine there.

Here is how I am setting the cookie. I've tried several different combinations of options here and they all have the same result.

res.cookie('jwt', token, {
    httpOnly: false,
    sameSite: false,
    signed: false,
    secure: false,
    encode: String
});
res.header('Access-Control-Allow-Credentials', 'true');

Edit: On the client side I am using Axios for my ajax requests.

Edit:

Here is the endpoint function for login (POST /api/users/login):

login: function(req, res) {
    User.findOne({
        $or: [{email: req.body.username}, {username: req.body.username}]
    }).exec().then(user => {
        if(user) {
            if(user.checkPassword(req.body.password)) {
                jwt.sign({ userID: user._id }, 'secret', (err, token) => {
                    res.cookie('jwt', token, {
                        httpOnly: false,
                        sameSite: false,
                        signed: false,
                        secure: false,
                        encode: String
                    });
                    res.header('Access-Control-Allow-Credentials', 'true');
                    res.status(200).send({ status: 'ok', message: 'Success'});
                });
            }
            else {
                return res.status(401).send({ status: 'error', message: 'Invalid username/password combination. Please try again.' });
            }
        }
        else {
            return res.status(401).send({ status: 'error', message: 'Invalid username/password combination. Please try again.' });
        }
    }, err => {
        return res.status(500).send({ status: 'error', message: 'unexpected error' });
    });
}

Here is the client-side login code:

login(username, password) {
    return new Promise((resolve, reject) => {
        axios({
            method: 'post',
            url: 'http://localhost:4001/api/users/login',
            data: {
                username,
                password
            }
        }).then((res) => {
            resolve(res.data);
        }, (err) => {
            reject(err);
        });
    });
}

Server is on port 4001, React CRA server is on 3000

chrispytoes
  • 1,714
  • 1
  • 20
  • 53
  • If you just want to verify express setting a cookie, please create a [mcve] - there's not a lot of code needed to set up the express server with a single app.get and cookie management. – Mike 'Pomax' Kamermans Nov 30 '18 at 21:52
  • 1
    @Mike'Pomax'Kamermans I'm not sure that's necessary. As I said, it works in production mode when the client and server are on the same port, so my server-side authentication code should be correct already. In development mode however, React CRA serves the client on its own server. So to not cause cross-origin problems in development, React CRA has a feature to set a "proxy" server to forward requests to. I suspect it's that feature which is not working correctly. – chrispytoes Nov 30 '18 at 21:58
  • If you don't think it's necessary, _it is necessary_. IT's something you do because while forming that mcve, you typically find the problem yourself, because you HAVE to find the problem in order to get to the point where it disappears if you remove enough code. mcve are not just "for us to help you", they are necessary work you should do as part of your own debugging process, and almost always in so doing you end up finding and fixing the problem yourself, even if you already had a SO post written. – Mike 'Pomax' Kamermans Nov 30 '18 at 21:59
  • @Mike'Pomax'Kamermans Alright, I get your point, I'll work on a full example. In the meantime I edited my original answer with my endpoint code and client-side request code to see if that might help. – chrispytoes Nov 30 '18 at 22:07

2 Answers2

10

To allow the browser to set cookies and adhere to Same Origin Policy, your client code should query http://localhost:3000/api/users/login (same host as client) instead of the (proxied) server url (port 4001).

You can also specify the base url as ${window.location.origin}/api or just /api.

Roy Wang
  • 11,112
  • 2
  • 21
  • 42
0

According to what riwu mentioned cookie domain should match, in my case setting "cookieDomainRewrite": "localhost", works

Below complete config for setupProxy.js in React:

const {createProxyMiddleware} = require('http-proxy-middleware');

module.exports = function (app) {
    app.use(
        '/api',
        createProxyMiddleware({
            target: 'http://localhost:8000',
            changeOrigin: true,
            cookieDomainRewrite: "localhost",
        })
    );
};
Mattia Fantoni
  • 889
  • 11
  • 15