Skip to content

This is a fork from auth0, as they have deprecated it on 31.08.2017 and reflects all the community input, dismissed by the original maintainers. It allows you to authenticate incoming socket.io connections with JWTs.

License

Notifications You must be signed in to change notification settings

Root-Core/socketio-jwt

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

This is a fork of Auth0s "socketio-jwt" library

It reassembles all the changes made by the community, that never got reviewed by Auth0.

These are most likely pull requests and changes by the owner of this fork. See here.

Build Status

Authenticate socket.io incoming connections with JWTs. This is useful if you are build a single page application and you are not using cookies as explained in this blog post: Cookies vs Tokens. Getting auth right with Angular.JS.

Installation

This fork will be released to the npm repository, but for now you can install directly from GitHub

npm install root-core/socketio-jwt

Example usage

// set authorization for socket.io
io.sockets
  .on('connection', socketioJwt.authorize({
    secret: 'your secret or public key',
    timeout: 15000 // 15 seconds to send the authentication message
  })).on('authenticated', function(socket) {
    //this socket is authenticated, we are good to handle more events from it.
    console.log('hello! ' + socket.decoded_token.name);
  });

Note: If you are using a base64-encoded secret (e.g. your Auth0 secret key), you need to convert it to a Buffer: Buffer('your secret key', 'base64')

Client side:

var socket = io.connect('http://localhost:9000');
socket.on('connect', function () {
  socket
    .emit('authenticate', {token: jwt}) //send the jwt
    .on('authenticated', function () {
      //do other things
    })
    .on('unauthorized', function(msg) {
      console.log("unauthorized: " + JSON.stringify(msg.data));
      throw new Error(msg.data.type);
    })
});

One roundtrip

The previous approach uses a second roundtrip to send the jwt, there is a way you can authenticate on the handshake by sending the JWT as a query string, the caveat is that intermediary HTTP servers can log the url.

var io            = require('socket.io')(server);
var socketioJwt   = require('socketio-jwt');

//// With socket.io < 1.0 ////
io.set('authorization', socketioJwt.authorize({
  secret: 'your secret or public key',
  handshake: true
}));
//////////////////////////////

//// With socket.io >= 1.0 ////
io.use(socketioJwt.authorize({
  secret: 'your secret or public key',
  handshake: true
}));
///////////////////////////////

io.on('connection', function (socket) {
  // in socket.io < 1.0
  console.log('hello!', socket.handshake.decoded_token.name);

  // in socket.io 1.0
  console.log('hello! ', socket.decoded_token.name);
})

For more validation options see auth0/jsonwebtoken.

Client side:

Append the jwt token using query string:

//// token part of query string ////
var socket = io.connect('http://localhost:9000', {
  'query': 'token=' + your_jwt
});


//// token coming in as Authorization Header ////
var socket = io.connect('http://localhost:9000', {
  'extraHeaders': { Authorization: `Bearer ${your_jwt}` }
});

Authorization Header Requirement

Require Bearer Tokens to be passed in as an Authorization Header

Server side:

io.use(socketioJwt.authorize({
  secret: 'your secret or public key',
  handshake: true,
  auth_header_required: true
}));

Handling token expiration

Server side:

When you sign the token with an expiration time:

var token = jwt.sign(user_profile, jwt_secret, {expiresInMinutes: 60});

Your client-side code should handle it as below.

Client side:

socket.on('unauthorized', function(error) {
  if (error.data.type == 'UnauthorizedError' || error.data.code == 'invalid_token') {
    // redirect user to login page perhaps?
    console.log('Users token has expired');
  }
});

Handling invalid token

Token sent by client is invalid.

Server side:

No further configuration needed.

Client side:

Add a callback client-side to execute socket disconnect server-side.

socket.on('unauthorized', function(error, callback) {
  if (error.data.type == 'UnauthorizedError' || error.data.code == 'invalid_token') {
    // redirect user to login page perhaps or execute callback:
    callback();
    console.log('Users token has expired');
  }
});

Server side:

To disconnect socket server-side without client-side callback:

io.sockets.on('connection', socketioJwt.authorize({
  secret: 'secret goes here',
  // No client-side callback, terminate connection server-side
  callback: false
}))

Client side:

Nothing needs to be changed client-side if callback is false.

Server side:

To disconnect socket server-side while giving client-side 15 seconds to execute callback:

io.sockets.on('connection', socketioJwt.authorize({
  secret: 'secret goes here',
  // Delay server-side socket disconnect to wait for client-side callback
  callback: 15000
}))

Your client-side code should handle it as below.

Client side:

socket.on('unauthorized', function(error, callback) {
  if (error.data.type == 'UnauthorizedError' || error.data.code == 'invalid_token') {
    // redirect user to login page perhaps or execute callback:
    callback();
    console.log('Users token has expired');
  }
});

Getting the secret dynamically

You can pass a function instead of an string when configuring secret. This function receives the request, the decoded token and a callback. This way, you are allowed to use a different secret based on the request and / or the provided token.

Server side:

var SECRETS = {
  'user1': 'secret 1',
  'user2': 'secret 2'
}

io.use(socketioJwt.authorize({
  secret: function(request, decodedToken, callback) {
    // SECRETS[decodedToken.userId] will be used as a secret or
    // public key for connection user.

    callback(null, SECRETS[decodedToken.userId]);
  },
  handshake: false
}));

Contribute

You are always welcome to open an issue or provide a pull-request!

Also check out the unit tests:

npm test

Issue Reporting

If you have found a bug or if you have a feature request, please report them at this repository issues section. Please do not report security vulnerabilities on the public GitHub issue tracker. The Responsible Disclosure Program details the procedure for disclosing security issues.

Original author

Auth0

Differences to Auth0-repo

  • Typescript support (Typings)
  • Fixed authentication in namspaces
    • With an more correct approach to get the header in the first place!
  • The encoded JWT is stored in socket.encoded_token
    • The propertys name is configurable via encodedPropertyName in the option object
    • Just like the decoded property name via decodedPropertyName in the option object
  • Exporting UnauthorizedError allows to throw own rejections / control flow
  • Added auth_header_required to option object to reject clients without an authentication header
  • Typos fixed, renamed variables
  • Removed empty example folder
  • Updated dependencies
  • Improved test coverage

License

This project is licensed under the MIT license. See the LICENSE file for more info.

About

This is a fork from auth0, as they have deprecated it on 31.08.2017 and reflects all the community input, dismissed by the original maintainers. It allows you to authenticate incoming socket.io connections with JWTs.

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • JavaScript 100.0%