3

I am getting a 403 on broadcasting/auth route.

I am using Laravel websockets package for web sockets and I have the below setup

- Backend Laravel server ( on port 8000 )
- Laravel Websockets Server ( laravel is running on 8001 and websocket server on 6001 )
- Standalone Ionic React app ( on port 8100)

Now everything works when I am trying Public Channel but when I try Private Channel it fails.

in the above screenshot of Laravel sockets dashboard I can see connection made as this is the same socketId which went with the request.

I am using Laravel Sanctum for Authentication.

PFB :- my client side code

const headers = {
    'Content-Type': 'application/json',
    Authorization: window.localStorage.getItem('token')
      ? `Bearer ${window.localStorage.getItem('token')}`
      : '',
    'Access-Control-Allow-Credentials': true,
  };
  const options = {
    broadcaster: 'pusher',
    key: 'abcsock',
    authEndpoint: 'http://localhost:8001/api/broadcasting/auth',
    authorizer: (channel, options) => {
      return {
        authorize: (socketId, callback) => {
          axios
            .post(
              'http://localhost:8001/api/broadcasting/auth',
              {
                socket_id: socketId,
                channel_name: channel.name,
              },
              { headers, withCredentials: true }
            )
            .then((response) => {
              callback(false, response.data);
            })
            .catch((error) => {
              callback(true, error);
            });
        },
      };
    },
    wsHost: '127.0.0.1',
    wsPort: 6001,
    encrypted: true,
    disableStats: true,
    enabledTransports: ['ws', 'wss'],
    forceTLS: false,
  };

Code from Laravel Websockets Server

api.php file

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\Broadcast;

Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
    return $request->user();
});

Broadcast::routes(['middleware' => ['auth:sanctum']]);

BroadcastServiceProvider.php

namespace App\Providers;

use Illuminate\Support\Facades\Broadcast;
use Illuminate\Support\ServiceProvider;

class BroadcastServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        Broadcast::routes(['middleware' => ['auth:sanctum']]);

        require base_path('routes/channels.php');
    }
}

NewNotification.php

<?php

namespace App\Notifications;

use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Messages\BroadcastMessage;
use Illuminate\Notifications\Notification;
use Illuminate\Broadcasting\PrivateChannel;

use App\AppNotification;
use App\User;

class NewNotification extends Notification
{
    use Queueable;

    public $notification;
    public $user;
    /**
     * Create a new notification instance.
     *
     * @return void
     */
    public function __construct(AppNotification $notification, User $user)
    {
        $this->notification = $notification;
        $this->user = $user;
    }

    /**
     * Get the notification's delivery channels.
     *
     * @param  mixed  $notifiable
     * @return array
     */
    public function via($notifiable)
    {
        return ['database', 'broadcast'];
    }

    /**
     * Get the array representation of the notification.
     *
     * @param  mixed  $notifiable
     * @return array
     */
    public function toArray($notifiable)
    {
        $notification = $this->notification;
        return [
            'id' => $notification->id,
            'title' => $notification->title,
            'description' => $notification->description,
            'button_text' => $notification->button_text,
            'is_read' => $notification->is_read,
            'created_at' => $notification->created_at,
            [
                'notification_for' => $notification->notifiable
            ]
        ];
    }

    public function toBroadcast($notifiable)
    {
        return new BroadcastMessage($this->toArray($notifiable));
    }

    public function broadcastOn()
    {
        return new PrivateChannel('Notifications.' . $this->user->id);
    }
}

channels.php

<?php

use Illuminate\Support\Facades\Broadcast;

Broadcast::channel('Notifications.{id}', function ($user, $id) {
    return (int) $user->id === (int) $id;
}, ['guards' => ['sanctum']]);

I have attached whatever I felt was sufficient to explain the issue and for you to troubleshoot but if you need anything more please ask.

Any help will be appreciated

Dhaval Chheda
  • 4,637
  • 5
  • 24
  • 44
  • Hello @Dhaval, please could you check this https://stackoverflow.com/questions/65856972/laravel-echo-sanctum-websockets-pusher-nuxtjs-spa I really need your help. Thanks – BKF Jan 23 '21 at 08:29

3 Answers3

0

i liked this package because its wrote in php but its sortof abandon and a lot of issues that not solved.

alternative of this server is laravel-echo-server(as laravel docs recommends) very easier to use. https://stackoverflow.com/a/61704796/7908390 more info if you can work with laravel-sockets im glad to see a new example

TEFO
  • 1,432
  • 9
  • 23
0

Have you added authentication for your channels.php? Please refer here: https://laravel.com/docs/7.x/broadcasting#defining-authorization-routes What does it look like?

I think you are doing everything correctly but you have not authorized your routes.

  • Yes I have added those in api.php and BroadcastServiceProvider as in the code snippets pasted above – Dhaval Chheda May 31 '20 at 18:00
  • yes but i mean, what does your channels.php look like? I cannot see it. – TonyPepperoni May 31 '20 at 23:08
  • I also do not know what events you are emitting and on what channel. – TonyPepperoni May 31 '20 at 23:12
  • I have added code of channels.php and NewNotification ( the notification that I want to send ) .. Once again its issue with PrivateChannel and not normal Channel. – Dhaval Chheda Jun 01 '20 at 04:13
  • Try: Broadcast::routes(["prefix" => "api", "middleware" => ["auth:sanctum"]]); I don't think you need the guard. You also mentioned websocket server is running on 8001. Shouldn't you point it to your backend (8000) for /api/broadcasting/auth – TonyPepperoni Jun 01 '20 at 16:26
  • Basically the flow is this: You login with echo.js with your auth token, laravel-websocket-server receives your token, and does a request to api/broadcasting/auth which is on your backend using your token string, if this jwt is correct, you will login as a user and your channel access will be granted if and only if your channels.php rules match. You will get another token and you use this to get all proceeding events. – TonyPepperoni Jun 01 '20 at 16:31
  • so I have 2 separate servers 1 for websockets and 1 for backend so I am authenticating against my backend server? – Dhaval Chheda Jun 01 '20 at 17:20
  • Correct. That's what determines authentication. So i'll explain again. 1) laravel echo will take your token and use it to send a request to backend/api/broadcasting/auth 2) this will return a token back which will be a token you use for echo Please check the in-depth explanation here: https://mattstauffer.com/blog/introducing-laravel-echo/ There is a part where he mentions the 403. – TonyPepperoni Jun 01 '20 at 20:02
0

I had the similar error with Laravel & pusher. I have fixed using decodeURIComponent and moving BroadCast::routes(['middleware' => ['auth:sanctum']]) to routes/api.php from BroadcastServiceProvider.php

Also, add withCredentials = true.

const value = `; ${document.cookie}`
const parts = value.split(`; XSRF-TOKEN=`)
const xsrfToken = parts.pop().split(';').shift()
Pusher.Runtime.createXHR = function () {
                    const xhr = new XMLHttpRequest()
                    xhr.withCredentials = true
                    return xhr
                }
const pusher = new Pusher(`${process.env.REACT_APP_PUSHER_APP_KEY}`,
                    {
                        cluster: 'us3',
                        authEndpoint: `${process.env.REACT_APP_ORIG_URL}/grooming/broadcasting/auth`,
                        auth: {
                            headers: {
                                Accept: 'application/json, text/plain, */*',
                                'X-Requested-With': 'XMLHttpRequest',
                                'X-XSRF-TOKEN': decodeURIComponent(xsrfToken)
                            }
                        }
                    })

                

I hope this helps a bit.

shud L
  • 36
  • 2