Install pusher

composer require pusher/pusher-php-server

Add environment variables

PUSHER_APP_ID=
PUSHER_APP_KEY=
PUSHER_APP_SECRET=
PUSHER_HOST=//not needed
PUSHER_PORT=443
PUSHER_SCHEME=https
PUSHER_APP_CLUSTER=mt1

Make sure you change the Broadcast driver to pusher

BROADCAST_DRIVER=pusher

Go to “config/app.php” and uncomment the following line:

App\Providers\BroadcastServiceProvider::class,

Create an Event

php artisan make:event MessageReceived

Inside of Event class

<?php

namespace App\Events;

use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;

class MessageReceived implements ShouldBroadcast
{
    use Dispatchable, InteractsWithSockets, SerializesModels;

    public $message;
    public $account_id;
    public $conversation_id;

    /**
     * Create a new event instance.
     *
     * @return void
     */
    public function __construct($account_id, $conversation_id, $message)
    {
        $this->message = $message;
        $this->account_id = $account_id;
        $this->conversation_id = $conversation_id;
    }

    /**
     * Get the channels the event should broadcast on.
     *
     * @return \Illuminate\Broadcasting\Channel|array
     */
    public function broadcastOn()
    {
        return new PrivateChannel('account.' . $this->account_id);
    }

    public function broadcastAs()
    {
        return 'message-received';
    }
}

Create endpoint for Pusher to authenthicate with Laravel Passport

/**
 * Authenticates logged-in user in the Pusher JS app
 * For private channels
 * @throws PusherException
 */
public function pusherAuth(Request $request)
{
    parse_str($request->getContent(), $output);

    $user = auth()->user();

    if($output['channel_name'] !== 'private-account.' . $user->account_id){
        return response([
            'message' => 'Not authorized to access this channel'
        ], 403);
    }
    $key = env('PUSHER_APP_KEY');
    $secret = env('PUSHER_APP_SECRET');
    $app_id = env('PUSHER_APP_ID');
    if ($user) {
        $pusher = new Pusher($key, $secret, $app_id);
        $auth = $pusher->authorizeChannel($output['channel_name'], $output['socket_id']);
        return response($auth, 200);
    } else {
        return response([
            'message' => 'Not authorized to access this channel'
        ], 403);
    }
}

Add route to function created above

Route::post('pusher', [AuthController::class, 'pusherAuth']);

Have Javascript subscribe to an account channel.

Import the library

<script src="https://js.pusher.com/8.0/pusher.min.js"></script>

Subscribe:

var pusher = new Pusher('{{PUSHER_KEY}}', {
  cluster: 'mt1',
  broadcaster: 'pusher',
  channelAuthorization: {
    endpoint: '{{API_ENDPOINT}}auth/pusher',//to endpoint created above
    forceTLS: false,
    encrypted: true,
    headers: {
      'Content-Type': 'application/json',
      'Accept': 'application/json',
      'Authorization': 'Bearer {{BEARER_TOKEN}}'
    }
  },
});

var channel = pusher.subscribe('private-account.{{ACCOUNT_UUID}}');
channel.bind('message-received', function (data) {//needs to be the same as the broadcast as
  console.log('data')
  console.log(data)
});

Send a message

event(new MessageReceived($conversation->account_id, $conversation->conversation_id, $request->Body));

REMEMBER: Events in Laravel are broadcast with the default queue configuration. If you have horizon installed in your application, then you need to run horizon to trigger the events.

Troubleshooting

  • If the front end is not calling the authentication endpoint:
    • Check that the cluster matches the application key used. For some reason it won’t throw an error, it will just fail silently and just not call the authentication endpoint.

Leave a Reply

Your email address will not be published. Required fields are marked *