Skip to content

Instantly share code, notes, and snippets.

@jfromaniello
Last active February 6, 2022 03:53
Show Gist options
  • Star 23 You must be signed in to star a gist
  • Fork 6 You must be signed in to fork a gist
  • Save jfromaniello/4087861 to your computer and use it in GitHub Desktop.
Save jfromaniello/4087861 to your computer and use it in GitHub Desktop.
socket-io.client send the cookies!
/*
* Little example of how to use ```socket-io.client``` and ```request``` from node.js
* to authenticate thru http, and send the cookies during the socket.io handshake.
*/
var io = require('socket.io-client');
var request = require('request');
/*
* This is the jar (like a cookie container) we will use always
*/
var j = request.jar();
/*
* First I will patch the xmlhttprequest library that socket.io-client uses
* internally to simulate XMLHttpRequest in the browser world.
*/
var originalRequest = require('xmlhttprequest').XMLHttpRequest;
require('xmlhttprequest').XMLHttpRequest = function(){
originalRequest.apply(this, arguments);
this.setDisableHeaderCheck(true);
var stdOpen = this.open;
/*
* I will patch now open in order to set my cookie from the jar request.
*/
this.open = function() {
stdOpen.apply(this, arguments);
var header = j.get({ url: 'http://localhost:9000' })
.map(function (c) {
return c.name + "=" + c.value;
}).join("; ");
this.setRequestHeader('cookie', header);
};
};
/*
* Authenticate first, doing a post to some url
* with the credentials for instance
*/
request.post({
jar: j,
url: 'http://localhost:9000/login',
form: {username: 'jose', password: 'Pa123'}
}, function (err, resp, body){
/*
* now we can connect.. and socket.io will send the cookies!
*/
var socket = io.connect('http://localhost:9000');
socket.on('connect', function(){
console.log('connected! handshakedddddddddddd')
done();
}));
});
@chrylarson
Copy link

Thank you so much for your work on Passport-SocketIO!

I had to change line 20 to use the xmlhttprequest bundled with socket.io-client.
Line 20: require('socket.io-client/node_modules/xmlhttprequest').XMLHttpRequest = . . .

@RunnerRick
Copy link

FYI. This no longer works with the latest version of xmlhttprequest. The code fails at line 22, because this.setDisableHeaderCheck is undefined.

And because you can't disable the header check, you can't set the cookie as the cookie header is a header you're not supposed to tamper with; xmlhttprequest prevents you from doing so.

@jabclab
Copy link

jabclab commented Dec 3, 2013

Thanks a lot for this. I ran into the same issue as @RunnerRick. The fix/hack I used was as follows:

$ npm install xmlhttprequest --save-dev
var orig = require('xmlhttprequest');

require('$root/node_modules/socket.io-client/node_modules/xmlhttprequest').XMLHttpRequest = function () {
  orig.apply(this, arguments);

  // etc.
};

i.e. effectively override the xmlhttprequest version used by socket.io-client (1.4.2 for 0.9.16) with the latest (1.6.0 at time of writing).

In addition I needed to specify force new connection as true in each of my tests else I was seeing timeouts, e.g.

it('should do something', function (done) {
    var sock = io.connect('xxx', { 'force new connection': true });
    // ...
    done();
});

it('should do something else', function (done) {
    var sock = io.connect('xxx', { 'force new connection': true });
    // ...
    done();
});

@codecowboy
Copy link

I'm getting errors. Can you post your version of the code?

        var header = j.get({ url: 'http://localhost:9000' })
                       ^
TypeError: Object #<CookieJar> has no method 'get'

@jabclab
Copy link

jabclab commented Jan 10, 2014

@codecowboy which version of request are you using?

@RJTM
Copy link

RJTM commented Feb 7, 2014

TypeError: Object # has no method 'get'

I'm getting this error with request v 2.33.0
How can I fix this?

@deleter8
Copy link

You've probably figured it out by now, but here's the answer in case anyone else (such as myself) stumbles across this thread:

replace:

var header = j.get({ url: 'http://localhost:9000' })
      .map(function (c) {
        return c.name + "=" + c.value;
      }).join("; ");

with:

var header = j.getCookieString('http://localhost:9000');

to reflect changes in the underlying cookie stores

@yamsellem
Copy link

I've made this works for socket.io-client ~0.9.

Is there a way to make it work for ~1.2 release?
Does it already?

@SystemParadox
Copy link

Some more hackery will be required for socket.io 1.0 as it does require('xmlhttprequest') directly, rather than require('xmlhttprequest').XMLHttpRequest, making it even more difficult to override.

@chrahunt
Copy link

I was having the same issue and was able to get cookies working with 1.2.1 by creating a file containing the following:

// XMLHttpRequest to override.
var xhrPath = 'socket.io-client/node_modules/engine.io-client/node_modules/xmlhttprequest';

// Make initial call to require so module is cached.
require(xhrPath);

var name = require.resolve(xhrPath);
// Get cached version.
var cachedXhr = require.cache[name];
var stdXhr = cachedXhr.exports;

// Callbacks exposes an object that callback functions can be added to.
var callbacks = {};
// Example
callbacks.test = function() {
  console.log("In callback.");
}

var newXhr = function() {
  stdXhr.apply(this, arguments);
  for (method in callbacks) {
    if (typeof callbacks[method] == "function") {
      callbacks[method].apply(this, arguments);
    }
  }
}

newXhr.XMLHttpRequest = newXhr;

cachedXhr.exports = newXhr;
module.exports = newXhr;
module.exports.callbacks = callbacks;

Requiring that and adding methods to callbacks ends up with a similar result to the original post, at least while the cached version of XMLHttpRequest persists. The require statement for the above file should be made before socket.io-client is required. An example, which adds the cookie to the request:

var myXhr = require('./xmlhttprequest');
var io = require('socket.io-client');

/*
  ... 
  code which ends up with cookie value in myCookie
  ...
*/

myXhr.callbacks.test2 = function() {
  this.setDisableHeaderCheck(true);
  var stdOpen = this.open;
  this.open = function() {
    stdOpen.apply(this, arguments);
    this.setRequestHeader('cookie', myCookie);
  }
}
// Assuming uri was defined somewhere above.
var socket = io.connect(uri);

It would also be possible, in the first file, to call stdXhr.XMLHttpRequest instead of stdXhr, in which case I believe the original solution (along with the few edits mentioned in the comments here) would work.

@mouradhamoud
Copy link

Thank you chrahunt this is what I was looking for 😄

@hackdan
Copy link

hackdan commented May 5, 2015

Complete example

For anyone that look for a complete example here is:

First create a file: client-code.js

process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
var request = require('request');
var cookies = request.jar();
var myXhr = require('./newXhr');

console.log('');

console.log('Request https://yourdomain.com/');
request.get(
    {
        url: 'https://yourdomain.com/',
        jar: cookies
    },
    function (err, res, body) {
        if (err) {
            console.log('Error: ', err);
            process.exit(0);
        }
        console.log('Status code: ', res.statusCode);
        console.log('');
        console.log('Headers: ', res.headers);
        console.log('');
        console.log('Cookies: ', cookies);
        console.log('');

        request.post(
            {
                url: 'https://yourdomain.com/login',
                jar: cookies,
                form: {
                    nick: 'secret_user',
                    password: 'yourpassword'
                }
            },
            function (err, res, body) {
                console.log('');
                if (err) {
                    console.log('Error: ', err);
                    process.exit(0);
                }
                console.log('Status code: ', res.statusCode);
                console.log('');
                console.log('Headers: ', res.headers);
                console.log('');
                console.log('Cookies: ', cookies);
                console.log('');


                myXhr.callbacks.test2 = function () {
                    this.setDisableHeaderCheck(true);
                    var stdOpen = this.open;
                    this.open = function () {
                        stdOpen.apply(this, arguments);
                        this.setRequestHeader('Cookie', res.headers['set-cookie'][0].split(';')[0]);
                    }
                }

                var io = require("socket.io-client")("https://yourdomain.com/audience", {forceNew: true});

                io.on('connect', function () {
                    console.log("Eureka !!!");
                });

                //process.exit(0);
            });
    }
);

Now create new file called:newXhr.js thank you @chrahunt

// XMLHttpRequest to override.
var xhrPath = 'socket.io-client/node_modules/engine.io-client/node_modules/xmlhttprequest';

// Make initial call to require so module is cached.
require(xhrPath);

var name = require.resolve(xhrPath);
// Get cached version.
var cachedXhr = require.cache[name];
var stdXhr = cachedXhr.exports;

// Callbacks exposes an object that callback functions can be added to.
var callbacks = {};
// Example
callbacks.test = function () {
    console.log("In callback.");
}

var newXhr = function () {
    stdXhr.apply(this, arguments);
    for (method in callbacks) {
        if (typeof callbacks[method] == "function") {
            callbacks[method].apply(this, arguments);
        }
    }
}

newXhr.XMLHttpRequest = newXhr;

cachedXhr.exports = newXhr;
module.exports = newXhr;
module.exports.callbacks = callbacks;

The dependencies package.json

{
    "name": "client-code",
    "version": "1.0.0",
    "description": "",
    "main": "client-code.js",
    "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1"
    },
    "dependencies": {
        "request": "^2.55.0",
        "socket.io-client": "^1.1.0",
        "xmlhttprequest": "^1.5.0"
    }
}

In Windows
Now run this in the promt:
set DEBUG=* & node client-code.js

Unix base
Now run this in the shell/terminal:
DEBUG=* node client-code.js

Tested with socket.io v1.1.0

Cheers!

@sjogren
Copy link

sjogren commented Aug 4, 2015

I fixed the cookie issue in socket-io-client 1.3.6 by overriding the http.ClientRequest constructor.

var ClientRequestOrig = require('http').ClientRequest;

// Override the ClientRequest constructor to set your cookie.
http.ClientRequest = function(options, cb) {

  // Set your cookie
  options.headers.Cookie = 'mycookie=mycookievalue';

  return new ClientRequestOrig(options, cb);

};

@Pyrolistical
Copy link

This solution does not work when you have multiple socket.io clients in the same node instance. If the clients auth as different users, overriding the cookie in the common xmlhttprequest makes all the clients appear to be the same

@DigitalZebra
Copy link

For those that are still ending up here, using a newer version of SocketIO client may be better for you. This pull requests outlines how you can set cookies using the library directly: rakeshok/socket.io-client-cookie#9

Posting the code from the pull request here as well:

const io = require('socket.io-client');

const cookie = 'connect.sid=xyz';
const socket = io(url, { path, extraHeaders: { cookie } `});

@ilatypov
Copy link

ilatypov commented Dec 1, 2020

Sending cookies across origins is disabled in browsers (I believe at a lower level than any patching could allow).

<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>Cookies</title>
    <script>
        window.addEventListener('load', function(event) {
            let exist = document.getElementById("exist");
            exist.textContent = document.cookie;

            let mycookie = "mysession=abc123";
            let elem = document.getElementById("cook");
            elem.textContent = mycookie;

            document.cookie = mycookie;

            // let url = "https://www.google.ca/";
            let url = "http://localhost:30080/";
            let urlelem = document.getElementById("url");
            urlelem.textContent = url;

            let respelem = document.getElementById("resp");

            let req = new Request(url, { credentials: "same-origin" });
            fetch(req).then(function(resp) {
                return resp.text();
            }).then(function(text) {
                respelem.textContent = text;
            });
        });
    </script>
</head>
<body>
    <p>
    Existing document cookies <code id="exist"></code>.
    <p>
    Sending a cookie <code id="cook"></code> to URL <code id="url"></code>.
    <p>
    Response text: <code id="resp"></code>.
</body>
</html>
Existing document cookies .

Sending a cookie mysession=abc123 to URL http://localhost:30080/.

Response text: <!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> [...] </body> </html> . 

The browser's console shows the cookie being sent (when using the same origin as a destination).

GET / HTTP/1.1
Host: localhost:30080
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:84.0) Gecko/20100101 Firefox/84.0
Accept: */*
Accept-Language: en-CA,en-US;q=0.7,en;q=0.3
Accept-Encoding: gzip, deflate
Referer: http://localhost:30080/f.html
DNT: 1
Connection: keep-alive
Cookie: mysession=abc123

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment