Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

brti's avatar
Level 3

Broadcasting Event to Specific Client

What's an easy way of broadcasting an event to a certain client, or a group of clients with socketio, and nodeJS? I have tried to get the Laravel session ID from the cookie with nodeJS but was not successful in doin so.

Here's the code in my socket.js file that handles broadcasting the data to the channel.

redis.psubscribe('*', function(error, count) {});

redis.on('pmessage', function(subscribed, channel, message) {
    console.log(message);

    message = JSON.parse(message);

    io.emit(channel + ':' + message.event, message.data);
});

One way I thought of doing it was by passing through an array of session IDs to the event object and then checking in the socket.js file to see if the current client has a matching session ID. It would probably end up looking something like this:

socket.js file:

message.data.receivers.forEach(receiver) {
    if (receiver == sessionID) {
        io.emit(channel + ':' + message.event, message.data);
    }
}

However this would rely on having to get the session ID from the cookie sent in the request via nodeJS, which I have not been able to do.

Any suggestions?

0 likes
22 replies
zefman's avatar

Hi,

We successfully did this before the new event broadcasting stuff was added. You are on the right track. You need to decrypt their cookie and then use it to retrieve the user's session, this is quite easy if you also use redis for sessions.

We have this code, which I think I got from a blog post somewhere, unfortuanatrly i can't find it now!


/**
 * Helper function to return ASCII code of character
 * @param  [string] string
 * @return [ascii code]
 */
function ord( string ) {
    return string.charCodeAt( 0 );
}

/**
 * This function retrieves the laravel session stored in redis
 * from a cookie
 * @param  [cookie] cookie
 * @return session
 */
function getSessionIdFromLaravelCookie( cookie ) {

    var cookie = JSON.parse( new Buffer( cookie, 'base64' ) );

    var iv     = new Buffer( cookie.iv, 'base64' );
    var value  = new Buffer( cookie.value, 'base64' );
    var key    = 'dd4u1wm2rVi82s6eOI8sTRzaWomob58x'; // laravel app key

    var rijCbc = new MCrypt( 'rijndael-128', 'cbc' );
    rijCbc.open( key, iv ); // it's very important to pass iv argument!

    var decrypted = rijCbc.decrypt( value ).toString();

    var len = decrypted.length - 1;
    var pad = ord( decrypted.charAt( len ) );

    var sessionId = PHPUnserialize.unserialize( decrypted.substr( 0, decrypted.length - pad ) );

    return sessionId;
}

After you have done this you should be able to get their session using from the redis db and then it should be fairly straight forward from there.

zefman's avatar

Oh i forgot you will need to install these dependencies of the node mycrypt library:

 apt-get install libmcrypt4 libmcrypt-dev
brti's avatar
Level 3

Thanks for the response. How would I go about getting the session info from the redis DB?

zefman's avatar
zefman
Best Answer
Level 3

Hi sorry for the slow reply, been very busy. This is what we have:

var cookieKey = 'login_82e5d2c56bdd0811318f0cf078b78bfc';

// On websocket connections
io.listen( server ).on( 'connection', function( client ) {

    const redisClient = redis.createClient();
    // Select the correct redis db
    redisClient.select( process.env.REDIS_DB, function() {});
    logger.info( 'Connected and listening to redis db ' + process.env.REDIS_DB + '.....' );

    // Get the laravel cookie
    var cookies        = cookie.parse( client.handshake.headers.cookie );
    var laravelSession = cookies.laravel_session;
    var sessionId      = 'laravel:' + getSessionIdFromLaravelCookie( laravelSession );
    var userId;

    // Find the user id
    redisClient.get( sessionId, function( err, session ) {
        try {
            client.laravelSession = PHPUnserialize.unserialize( PHPUnserialize.unserialize( session ) );
            connectedUsers[ client.laravelSession[ cookieKey ] ] = client;
        }
        catch ( err ) {
            logger.info( 'Error unserializing session!', err );
        }

        if ( client.laravelSession[ cookieKey ] ) {
            logger.info( 'User connected: ' + client.laravelSession[ cookieKey ] );
            logger.info( 'Total connected: ' + _.keys( connectedUsers ).length );
            userId = client.laravelSession[ cookieKey ];
        }

    } );

} );
1 like
brti's avatar
Level 3

I have a problem with retrieving the cookies. I run the code that you have put above:

var cookies = cookie.parse(client.handshake.headers.cookie);

console.log(cookies);

I then just dump out the output to see the cookies that are being passed in the request, then when I run the server this is the response I see from console.log(cookies);:

{ io: 'n60obMX5WfAuzEnpAAB' }

From the looks of the it neither the X-CSRF-TOKEN cookie, or the laravel_session cookie are being returned from client.handshake.headers.cookie, and I cannot understand why.

strategicsdemexico's avatar

is this post worked out? i don't exactly know where to place that code!

I would like to get some help!

zefman's avatar

Is your socket.io running at a different address/ip? It will only send the laravel cookie if you connect to it using the same domain. So if your site is example.com your node server will need to be at example.com: 8080 or whatever port it is you have it running on. If it is on the same domain it should send along the laravel cookie

brti's avatar
Level 3

So when setting up the socket.io connection I should put

var socket = io('socket.dev:8000');

Instead of:

var socket = io('192.168.10.10:8000');
brti's avatar
Level 3

Nope still none. Changed the socket.js file so it would listen on port 8000 instead of 3000, and that didn't work either.

zefman's avatar

The port number shouldn't matter, you can leave that as what you had before. Its just the domain thats important. So if you visit your laravel site are you using the address socket.dev?

brti's avatar
Level 3

Yeah I'm using the address socket.dev

zefman's avatar

Hmm I'm not sure then I'm afraid. This setup is working for us. If it is the same domain as your main laravel app the session cookie should be sent with the initial request, there must be something else wrong.

zefman's avatar

Great!!! Glad I could help, I had the eureka moment too the first time I got it to work!

1 like
tenantcloud's avatar

@zefman

According to your reply, all data will be passed to all userd, but encrypted. Am I right?

zefman's avatar

@ukietech The code there doesn't do any of the relaying of websocket data. It just retrieves the session so you know which user you are dealing with. Because you then have the user id it is then easy to broadcast messages intended for one user to that user. You just need to send a userid along with the event you pass to redis

1 like
tenantcloud's avatar

@zefman Thank you for your reply. I've done that functionality in another way - using JWT.

Can you give an example of how to broadcast user channel to appropriate user.

My code is

redis.psubscribe('*', function(err, count) {

    //
});

I guess check should be in this block. Thank you!

zefman's avatar

Hi @ukietech sorry for the slow reply, been busy.

So in Laravel I do something like this:

    $redis = \RedisL5::connection();
        $redis->publish( 'messaging', json_encode( [
            'conversation_id' => $conversation->id,
            'participants'    => $participants,
            'message'         => $message
        ] ) );

Where participants is a list of user ids that I want the message to go to.

Then in node I do something like this:

// Subscribe to the relevant redis channels
redisClient.subscribe( 'messaging' );

// On recieving a message from one of the channels we subscribed too.
// Note 'message' is just redis's term for recieving something from a 
// subscribed channel. This is note a message sent by a user on bfc
redisClient.on( 'message', function( channel, data ) {

    data = JSON.parse( data );

    // Check to see whether this user should recieve this message
    if ( data.participants.indexOf( client.laravelSession[ userIdKey ] ) > -1 ) {

        // Check the message is not from the connected user
        if ( data.message.user_id != client.laravelSession[ userIdKey ] ) {
            client.emit( channel, data );
        }

    }

} );

Basically it looks to see if the connected client is in the participants lists, if they are it emits the message to them. Is that what you were after?

1 like
zefman's avatar

@ukietech sweet will have a look. Interested to see how you have used JWTs. I had researched them for this purpose but never got round to trying anything out.

1 like

Please or to participate in this conversation.