You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
125 lines
5.2 KiB
125 lines
5.2 KiB
4 years ago
|
# socketio-auth [![Build Status](https://secure.travis-ci.org/facundoolano/socketio-auth.png)](http://travis-ci.org/facundoolano/socketio-auth)
|
||
|
|
||
|
This module provides hooks to implement authentication in [socket.io](https://github.com/Automattic/socket.io) without using querystrings to send credentials, which is not a good security practice.
|
||
|
|
||
|
Client:
|
||
|
```javascript
|
||
|
var socket = io.connect('http://localhost');
|
||
|
socket.on('connect', function(){
|
||
|
socket.emit('authentication', {username: "John", password: "secret"});
|
||
|
socket.on('authenticated', function() {
|
||
|
// use the socket as usual
|
||
|
});
|
||
|
});
|
||
|
```
|
||
|
|
||
|
Server:
|
||
|
```javascript
|
||
|
var io = require('socket.io').listen(app);
|
||
|
|
||
|
require('socketio-auth')(io, {
|
||
|
authenticate: function (socket, data, callback) {
|
||
|
//get credentials sent by the client
|
||
|
var username = data.username;
|
||
|
var password = data.password;
|
||
|
|
||
|
db.findUser('User', {username:username}, function(err, user) {
|
||
|
|
||
|
//inform the callback of auth success/failure
|
||
|
if (err || !user) return callback(new Error("User not found"));
|
||
|
return callback(null, user.password == password);
|
||
|
});
|
||
|
}
|
||
|
});
|
||
|
```
|
||
|
|
||
|
The client should send an `authentication` event right after connecting, including whatever credentials are needed by the server to identify the user (i.e. user/password, auth token, etc.). The `authenticate` function receives those same credentials in 'data', and the actual 'socket' in case header information like the origin domain is important, and uses them to authenticate.
|
||
|
|
||
|
## Configuration
|
||
|
|
||
|
To setup authentication for the socket.io connections, just pass the server socket to socketio-auth with a configuration object:
|
||
|
|
||
|
```javascript
|
||
|
var io = require('socket.io').listen(app);
|
||
|
|
||
|
require('socketio-auth')(io, {
|
||
|
authenticate: authenticate,
|
||
|
postAuthenticate: postAuthenticate,
|
||
|
disconnect: disconnect,
|
||
|
timeout: 1000
|
||
|
});
|
||
|
```
|
||
|
|
||
|
The supported parameters are:
|
||
|
|
||
|
* `authenticate`: The only required parameter. It's a function that takes the data sent by the client and calls a callback indicating if authentication was successfull:
|
||
|
|
||
|
```javascript
|
||
|
function authenticate(socket, data, callback) {
|
||
|
var username = data.username;
|
||
|
var password = data.password;
|
||
|
|
||
|
db.findUser('User', {username:username}, function(err, user) {
|
||
|
if (err || !user) return callback(new Error("User not found"));
|
||
|
return callback(null, user.password == password);
|
||
|
});
|
||
|
}
|
||
|
```
|
||
|
* `postAuthenticate`: a function to be called after the client is authenticated. It's useful to keep track of the user associated with a client socket:
|
||
|
|
||
|
```javascript
|
||
|
function postAuthenticate(socket, data) {
|
||
|
var username = data.username;
|
||
|
|
||
|
db.findUser('User', {username:username}, function(err, user) {
|
||
|
socket.client.user = user;
|
||
|
});
|
||
|
}
|
||
|
```
|
||
|
* `disconnect`: a function to be called after the client is disconnected.
|
||
|
|
||
|
```javascript
|
||
|
function disconnect(socket) {
|
||
|
console.log(socket.id + ' disconnected');
|
||
|
}
|
||
|
```
|
||
|
|
||
|
* `timeout`: The amount of millisenconds to wait for a client to authenticate before disconnecting it. Defaults to 1000. The value 'none' disables the timeout feature.
|
||
|
|
||
|
## Auth error messages
|
||
|
|
||
|
When client authentication fails, the server will emit an `unauthorized` event with the failure reason:
|
||
|
|
||
|
```javascript
|
||
|
socket.emit('authentication', {username: "John", password: "secret"});
|
||
|
socket.on('unauthorized', function(err){
|
||
|
console.log("There was an error with the authentication:", err.message);
|
||
|
});
|
||
|
```
|
||
|
|
||
|
The value of `err.message` depends on the outcome of the `authenticate` function used in the server: if the callback receives an error its message is used, if the success parameter is false the message is `'Authentication failure'`
|
||
|
|
||
|
```javascript
|
||
|
function authenticate(socket, data, callback) {
|
||
|
db.findUser('User', {username:data.username}, function(err, user) {
|
||
|
if (err || !user) {
|
||
|
//err.message will be "User not found"
|
||
|
return callback(new Error("User not found"));
|
||
|
}
|
||
|
|
||
|
//if wrong password err.message will be "Authentication failure"
|
||
|
return callback(null, user.password == data.password);
|
||
|
});
|
||
|
}
|
||
|
```
|
||
|
|
||
|
After receiving the `unauthorized` event, the client is disconnected.
|
||
|
|
||
|
## Implementation details
|
||
|
|
||
|
**socketio-auth** implements two-step authentication: upon connection, the server marks the clients as unauthenticated and listens to an `authentication` event. If a client provides wrong credentials or doesn't authenticate after a timeout period it gets disconnected. While the server waits for a connected client to authenticate, it won't emit any broadcast/namespace events to it. By using this approach the sensitive authentication data, such as user credentials or tokens, travel in the body of a secure request, rather than a querystring that can be logged or cached.
|
||
|
|
||
|
Note that during the window while the server waits for authentication, direct messages emitted to the socket (i.e. `socket.emit(msg)`) *will* be received by the client. To avoid those types of messages reaching unauthorized clients, the emission code should either be defined after the `authenticated` event is triggered by the server or the `socket.auth` flag should be checked to make sure the socket is authenticated.
|
||
|
|
||
|
See [this blog post](https://facundoolano.wordpress.com/2014/10/11/better-authentication-for-socket-io-no-query-strings/) for more details on this authentication method.
|