loading...
Cover image for Backend application + Socket.io
Bytesized Code

Backend application + Socket.io

diegoug profile image Diego Uribe Gamez ・3 min read

One of my abilities is to use socket.io along with a backend application (In this case, it is Django) to send real time events to the web browser.

The key is to use socket.io as a bridge of events without over-optimizing the infrastructure, by not handling the business logic on this bridge.

To perform this, It is necessary to follow the following steps:

  1. The user log in to the web application, when the backend dispatches the HTML access.
  2. The web application have to request access to the socket server.
  3. The socket server have to verify the authenticity of the user that is longing in with the backend.

Let’s do it.

The user log in to the web application, when the backend dispatches the HTML access.

Ok, At first the backend sends the django user to redis in a temporarily form, and secures it with a key.

Then, it dispatches the HTML along with the cookie that contains the key hat we used to secure the user in redis, giving as a result our template, as follows:

class IndexTemplateView(TemplateView):
    template_name = 'base.html'

    def get_context_data(self, **kwargs):
        context = super(IndexTemplateView, self).get_context_data(**kwargs)

        self.set_access_token()

        data = {
            'title': 'Connect nodejs with your app'
        }

        context.update(data)
        return context

    def set_access_token(self):
        self.token = get_random_string(32)
        key = 'session:%s' % str(self.token)
        data = {
            'user': 'diegoug'
        }
        value = json.dumps(data)
        redis.setex(key, 86400, value)

    def render_to_response(self, context, **response_kwargs):
        response = super(IndexTemplateView, self).render_to_response(context, **response_kwargs)
        response.set_cookie(key='nodejskey', value=self.token, max_age=86400, domain=os.environ.get('DJANGO_SERVER_NAME', ''), secure=None)
        return response

The web application have to request access to the socket server.

This step is the simplest, here the HTML javascript accesses the socket server and when it accesses correctly, it displays the user name on the screen. Here the HTML code:

<script src="{{request.socketio}}/socket.io/socket.io.js"></script>
<script type="text/javascript">
  window.nodeServer = '{{request.socketio}}';
</script>
<script>
  const socket = io(window.nodeServer);
  socket.on('join', data => {
    let string = 'message: '+ data + '<br>';
    document.getElementById('data').innerHTML += string;
  });
</script>
<h1>{{ title }}</h1>
<div id="data"></div>

The socket server have to verify the authenticity of the user that is longing in with the backend.

At this point the socket server have to take the key from the cookie and take out the data of redis, if this data exists, it allows the socket to access to the connection event, sending an event to a channel that notify the user.

// dependencies
const Io = require('socket.io');
const redis = require('redis');
const cookie = require('cookie');
const serialize = require('node-serialize');
const clientRedis = redis.createClient(process.env.REDIS_PORT, process.env.REDIS_HOST);
clientRedis.select(1);
// module
function Socket () {
    this.io = Io.listen(process.env.SOCKETIO_PORT);
}
// run socket
Socket.prototype.run = function () {
    // set auth
    this.io.use((socket, next) => {
       this.auth(socket, next);
    });
    // when a client connects
    this.io.sockets.on('connection', socket => {
        // event join
        socket.emit('join', socket.handshake.user);
    });
};
// authentication
Socket.prototype.auth = function (socket, next) {
    // get cookie token
    const userCookie = cookie.parse(socket.request.headers.cookie);
    // redis validation
    clientRedis.get('session:' + userCookie.nodejskey, (err, session) => {
        // error or not session
        if (err || !session) {
            return next(new Error('Not authorized.'));
        }
        // config session
        session = serialize.unserialize(session);
        socket.handshake.user = session.user;
        next();
    });
};

let socket = new Socket();
socket.run();

The result:
Done

From here and on, it is a matter of managing rooms, direct messages and keep using redis to send events between the backend and the socket server using channels.

An important step that needs to be done, is to verify the domain from where the user try to log in the socket, to avoid impersonation of the real user identity.

Bytesized Code

We help developers learn to code 😎 Screencasts, tutorials, and Byteconf, a free developer conference series streamed on YouTube!

Discussion

markdown guide