Setting Cookies with CORS requests

19,484

What wasn't immediately obvious is that cookies set by the server, at least in CORS requests, and possibly (probably) in all requests are limited to the same domain as the server.

index.js (Node.js Server)

const http = require('http');
const fs = require('fs');

// Pretty colors

const colors = {
  purple: '\033[95m',
  orange: '\033[93m',
  blue: '\033[97m',
  underline: '\033[4m',
  bold: '\033[1m',
  reset: '\033[0m'
}

const server = http.createServer(function (req, res) {

  //Console logs to verify what's getting hit. 

  console.log(colors.purple + colors.underline + 'Hit it!' + colors.reset);
  console.log(colors.orange + colors.bold + 'url:' + colors.reset, req.url);

  if (/\/cookie/.test(req.url)) {
    console.log(colors.blue + 'We need to cook(ie) Jesse\n' + colors.reset);
    // Generate a random string in a rather convoluted way.
    var randomStr = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER).toString(36) + 
    Math.floor(Math.random() * Number.MAX_SAFE_INTEGER).toString(36) + 
    Math.floor(Math.random() * Number.MAX_SAFE_INTEGER).toString(36);
    randomStr = new Buffer(randomStr.toString(), 'binary').toString('base64');
    // All .dev domains pointed to localhost via dnsmasq, though a hosts file
    // Update should also do the trick.
    res.writeHead(200, {
      'Set-Cookie': 'ajaxTestCookie=cookie' + randomStr + '; domain=.example.dev; HttpOnly',
      'Access-Control-Allow-Origin': 'http://example.dev:3999',
      'Access-Control-Allow-Credentials': 'true',
      'Access-Control-Allow-Methods': 'GET, POST',
      'Access-Control-Allow-Headers': 'Content-Type, Set-Cookie, *'
    });
    return res.end('OK!');
  }
  console.log(colors.blue + 'We\'re having fun at the HTML!\n' + colors.reset);
  // Send out html file. 
  fs.readFile('./cookies.html', function (err, data) {
    if (err) {
      res.writeHead(500);
      return res.end('Failure to launch!');
    }
    res.end(data.toString());
  });
});

server.listen(3999); // api.example.dev:3999, for example

cookies.html

<html>

<head>
  <title>Cookie Test</title>
</head>

<body>
  <button class="getCookie">Get Cookies!</button>
  <script>
    (function() {
      document.querySelector(".getCookie").addEventListener("click", function(e) {
        console.log("test");
        var req = new XMLHttpRequest();
        // Request succeeds, but cookie will not be set!
        // req.open("GET", "http://localhost:3999/cookie", true);
        /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
        // This line, however, will work, assuming this page is on
        // the same domain, or a subdomain of the same domain. 
        // (For example test.example.dev and api.example.dev)
        // As long as the Access-Control-Allow-Origin Header is
        // properly set to allow the domain.
        req.open("GET", "http://api.example.dev:3999/cookie", true);
        req.onload = function() {
          console.log(req.responseText);
        };
        req.withCredentials = true;
        req.send();
      });
    }());
  </script>
</body>
Share:
19,484
Brandon Anzaldi
Author by

Brandon Anzaldi

Lover of all things JavaScript and MEAN. Doctor Who, Black Mirror, Psych, Smallpools, Patrick Wolf, LCD Soundsystem, Clean Bandit, Walk The Moon, AWOLNATION, alt-J, Caravan Palace, The Lumineers. Talk to me, and shoot me an email at brandon at brandonanzaldi dot com. Unless explicitly stated/the code is attributed to another post or developer, any code written by me, shared in my answers is licensed under the WTFPL, though attribution is appreciated. #SOreadytohelp

Updated on June 04, 2022

Comments

  • Brandon Anzaldi
    Brandon Anzaldi almost 2 years

    I've been trying to tackle this problem for a few days. Setting cookies on CORS requests. I've seen conflicting articles and answers, some saying that as long as the XHR request has withCredentials set to true, and the server sends the appropriate headers, the browser should respect the Set-Cookie header. However, in my testing this has not been the case.

    Example Code:

    index.js (Node.js Server)

    const http = require('http');
    const fs = require('fs');
    
    // Pretty colors
    
    const colors = {
      purple: '\033[95m',
      orange: '\033[93m',
      blue: '\033[97m',
      underline: '\033[4m',
      bold: '\033[1m',
      reset: '\033[0m'
    }
    
    const server = http.createServer(function (req, res) {
    
      //Console logs to verify what's getting hit. 
    
      console.log(colors.purple + colors.underline + 'Hit it!' + colors.reset);
      console.log(colors.orange + colors.bold + 'url:' + colors.reset, req.url);
    
      if (/\/cookie/.test(req.url)) {
        console.log(colors.blue + 'We need to cook(ie) Jesse\n' + colors.reset);
    
        // Generate a random string in a rather convoluted way.
        var randomStr = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER).toString(36) + 
        Math.floor(Math.random() * Number.MAX_SAFE_INTEGER).toString(36) + 
        Math.floor(Math.random() * Number.MAX_SAFE_INTEGER).toString(36);
        randomStr = new Buffer(randomStr.toString(), 'binary').toString('base64');
    
        // All .dev domains pointed to localhost via dnsmasq, though a hosts file
        // Update should also do the trick.
        res.writeHead(200, {
          'Set-Cookie': 'ajaxTestCookie=cookie' + randomStr + '; Domain=.example.dev; HttpOnly',
          'Access-Control-Allow-Origin': 'http://example.dev:3999',
          'Access-Control-Allow-Credentials': 'true',
          'Access-Control-Allow-Methods': 'GET, POST',
          'Access-Control-Allow-Headers': 'Content-Type, Set-Cookie, *'
        });
        return res.end('OK!');
      }
    
      console.log(colors.blue + 'We\'re having fun at the HTML!\n' + colors.reset);
    
      // Send out html file. 
      fs.readFile('./cookies.html', function (err, data) {
        if (err) {
          res.writeHead(500);
          return res.end('Failure to launch!');
        }
        res.end(data.toString());
      });
    });
    
    server.listen(3999);
    

    cookies.html

    <html>
    
    <head>
      <title>Cookie Test</title>
    </head>
    
    <body>
      <button class="getCookie">Get Cookies!</button>
      <script>
        (function() {
          document.querySelector(".getCookie").addEventListener("click", function(e) {
            console.log("test");
            var req = new XMLHttpRequest();
            req.open("GET", "http://localhost:3999/cookie", true);
            req.onload = function() {
              console.log(req.responseText);
            };
            req.withCredentials = true;
            req.send();
          });
        }());
      </script>
    </body>
    
    </html>
    

    I've tried testing this on both Firefox Developer Edition and Chrome, and the cookies will not set unless the page is accessed directly.

    Is there anything I'm missing to get cookies working on CORS requests?