2

Even though this question is asked several times at SO like:

fetch: Getting cookies from fetch response

or

Unable to set cookie in browser using request and express modules in NodeJS

None of this solutions could help me getting the cookie from a fetch() response

My setup looks like this:

Client

export async function registerNewUser(payload) {
    return fetch('https://localhost:8080/register',
        {
            method: 'POST',
            body: JSON.stringify(payload),
            credentials: 'same-origin',
            headers: {
                Accept: 'application/json',
                'Content-Type': 'application/json'
            }
        });
}

...
function handleSubmit(e) {
    e.preventDefault();

    registerNewUser({...values, avatarColor: generateAvatarColor()}).then(response => {
        console.log(response.headers.get('Set-Cookie')); // null
        console.log(response.headers.get('cookie')); //null
        console.log(document.cookie); // empty string
        console.log(response.headers); // empty headers obj
        console.log(response); // response obj
    }).then(() => setValues(initialState))
}

server

private setUpMiddleware() {
    this.app.use(cookieParser());
    this.app.use(bodyParser.urlencoded({extended: true}));
    this.app.use(bodyParser.json());
    this.app.use(cors({
        credentials: true,
        origin: 'http://localhost:4200',
        optionsSuccessStatus: 200, // some legacy browsers (IE11, various SmartTVs) choke on 204
        credentials: true
    }));
    this.app.use(express.static(joinDir('../web/build')));
}
...
this.app.post('/register', (request, response) => {
    const { firstName, lastName, avatarColor, email, password }: User = request.body;
    this.mongoDBClient.addUser({ firstName, lastName, avatarColor, email, password } as User)
        .then(() => {
            const token = CredentialHelper.JWTSign({email}, `${email}-${new Date()}`);
            response.cookie('token', token, {httpOnly: true}).sendStatus(200); // tried also without httpOnly
        })
        .catch(() => response.status(400).send("User already registered."))
})

enter image description here

MarcoLe
  • 2,309
  • 6
  • 36
  • 74

2 Answers2

1

JavaScript fetch method won't send client side cookies and silently ignores the cookies sent from Server side Reference link in MDN, so you may use XMLHttpRequest method to send the request from your client side.

Magaesh
  • 488
  • 6
  • 19
0

I figured it out. The solution was to set credentials to 'include' like so:

export async function registerNewUser(payload) {
    return fetch('https://localhost:8080/register',
        {
            method: 'POST',
            body: JSON.stringify(payload),
            credentials: 'include',
            headers: {
                Accept: 'application/json',
                'Content-Type': 'application/json'
            }
        });
}

After that I needed to enabled credentials in my cors middleware:

    this.app.use(cors({
            credentials: true, // important part here
            origin: 'http://localhost:4200',
            optionsSuccessStatus: 200 
        })

And then finally I needed to remove the option {httpOnly: true} in the express route response:

response.cookie('token', '12345ssdfsd').sendStatus(200);

Keep in mind if you send the cookie like this, it is set directly to the clients cookies. You can now see that the cookie is set with: console.log(document.cookie).

But in a practical environment you don't want to send a cookie that is accessible by the client. You should usually use the {httpOnly: true} option.

halfer
  • 19,824
  • 17
  • 99
  • 186
MarcoLe
  • 2,309
  • 6
  • 36
  • 74