63

I'm trying to set a http header when socket.io client makes the connection request. Is there a way to do this?

Here is what i'm doing:

// server side
var io = socketio(server);

io.use(function (socket, next) {
  // authorize using authorization header in socket.request.headers
});

// client side
var socket = io();  // i'm trying to set an authorization header in this http reqeust

Any ideas? Thanks.

Ziyu
  • 1,047
  • 2
  • 10
  • 9
  • When socket client makes a request, it already has request headers with it. Are you saying that you want to change those or you just want to send some tokens from socket client and based on those tokens you want to authorize the client???? – M Omayr May 02 '14 at 09:23
  • I want to send a token in the header. @MOmayr – Ziyu May 02 '14 at 09:31
  • you can send that token in the query string and check it in authorization function. Should i give you an example? – M Omayr May 02 '14 at 09:36
  • @MOmayr Thanks. That's what I'm doing at the moment. But to put the token in the header is what I would like to do. Cause in that way i could reuse my auth middleware for socket.io connection request. – Ziyu May 02 '14 at 09:41
  • Well, I think you'll have to dig socket.io for that. But you can attach an object with socket in authorization function and later use it in io.connection event. – M Omayr May 02 '14 at 10:40
  • Did you ever figure this out? Working through it too. – Petrogad Oct 10 '14 at 13:37
  • You won't want to set any headers if your socket.io server is not on the same domain. For IE (8 at least) you cannot set headers on a cross domain ajax request. Using a query parameter and ensuring you use https is the good way to go. – Tug Nov 11 '14 at 17:39

8 Answers8

71

You can use extraHeaders option, if you are using socket.io-client >= 1.4.

For example:

var socket = io("http://localhost", {
  extraHeaders: {
    Authorization: "Bearer authorization_token_here"
  }
});

engine.io-client, which is a backend of socket.io-client, introduced extraHeaders support on 2015-11-28.

ymyzk
  • 923
  • 7
  • 12
  • 20
    While this appears to be a great option, it works when using the NodeJS client, but not in the browser. It states this in the read me, presumably b/c of the first link in the answer by bakavic above (not all transports allow for the setting of custom headers) – Sunil D. Feb 16 '16 at 20:08
  • It wasn't supported on some browsers as of yesterday due to a bug, I've fixed it :) – Lu4 Feb 02 '17 at 21:43
  • 5
    Awesome, how do you get the headers on the server side then? – TheJKFever Mar 28 '17 at 22:28
  • 6
    Unfortunately the extraHeaders are available only with Polling mode and not with websocket... – bertonc96 Sep 04 '19 at 15:49
  • Headers are only provided when the connection is over http(s) which is always true for the initial handshake. After that, if the client/server "upgrade" to ws(s), no headers are available. You will have to pass the auth data in the packets instead. – Julian Knight Dec 01 '19 at 12:57
  • Use query parameters instead `query: { token:'some-token-value' }` – Ray Foss May 07 '21 at 15:34
15

It seems like the client doesn't support setting headers, as not all transports allow for the setting of headers.

This post by facundoolano details a workaround to authentication that doesn't require placing the auth token in the query string.

His workaround module can be found at https://github.com/invisiblejs/socketio-auth.

Makes me wonder why on server-side, socket.io allows for the request headers to be accessed...

bakavic
  • 1,297
  • 10
  • 11
  • 3
    Socket.io handshake (even for WebSockets) starts with regular REST call and further elevation to WebSocket protocol, so the answer seems wrong, on the other hand if you are using HTTP Request Headers than this should work fine... – Lu4 Jan 23 '17 at 17:35
15

There's a new way to do this: https://socket.io/docs/v3/middlewares/. Look under the "Sending Credentials" section.

// client
const socket = io(server, {
    transports: ['websocket', 'polling', 'flashsocket'],
    auth: {
        token: 'abc'
    }
});

// server
io.use((socket, next) => {
    const token = socket.handshake.auth.token;
    if (isValidJwt(token)){
        next();
    }else{
        next(new Error("Socket authentication error"));
    }
});

async function isValidJwt(token){
    jwt.verify(token, secrets.jwt, function(err, decoded) {
        if (err){
            console.log(err);
            return false;
        }else{
            //console.log(decoded);
            return true;
        }
    });
}
BuffaloDev
  • 373
  • 2
  • 11
4

This following information has been deprecated since socket.io 1.0

There are two methods of authorization: global or namespace (think route). The global method is set on the server with the io.set('authorization', function (handshakeData, callback) configuration call.

The handshakeData object contains the following information:

{
   headers: req.headers       // <Object> the headers of the request
 , time: (new Date) +''       // <String> date time of the connection
 , address: socket.address()  // <Object> remoteAddress and remotePort object
 , xdomain: !!headers.origin  // <Boolean> was it a cross domain request?
 , secure: socket.secure      // <Boolean> https connection
 , issued: +date              // <Number> EPOCH of when the handshake was created
 , url: request.url          // <String> the entrance path of the request
 , query: data.query          // <Object> the result of url.parse().query or a empty object
}

The above information and a deeper explanation is available on this documentation page.

keza
  • 2,601
  • 2
  • 16
  • 10
adamrights
  • 1,701
  • 1
  • 11
  • 27
3

As of version 2.0.0 / 2017-01-22 engine.io-client supports

[feature] Allow extraHeaders to be set for browser clients in XHR requests (#519)

However at this point the socket.io-client is not updated to support this functionality, so couple of days may make this saga end until that time use the following instructions: https://facundoolano.wordpress.com/2014/10/11/better-authentication-for-socket-io-no-query-strings/

Lu4
  • 14,873
  • 15
  • 79
  • 132
2

"transportOptions" options can be used to send extra headers in socket.io request. I also explained that here :-

Node.js + Socket.io | Set custom headers on the server

jagjeet
  • 376
  • 4
  • 12
1

Short Answer: It's imposiburu based on spec... if you just need to pass info early... why not query parameters?

socket = io('localhost:5000', {
      path: '/mySocketPath',
      transports: ['websocket'],
      query: {
        token:'some-token-value'
      }
})

See @satpal-07 in https://github.com/socketio/socket.io-client/issues/1356#issuecomment-810023635

Ray Foss
  • 3,649
  • 3
  • 30
  • 31
1

For some reason, these request headers are only received if the socket server is also socket.io. If I connect to a python Websockets server for example I have no luck authenticating.

The only solution that worked for me is to use a different WebSocket client, for example, ws works fine.

import WebSocket from 'ws';

const socket = new WebSocket('wss://example.com/path', {
      headers: {
            Authorization: 'token'
         },
 });
  • 1
    Is there any way to handle `ws does not work in the browser. Browser clients must use the native WebSocket object` error? – sj_959 Aug 24 '21 at 05:35