29

I have recently delved into Laravel 5.3's Laravel-Echo and Pusher combination. I have successfully set up public channels and moved on to private ones. I am having trouble with Laravel returning a 403 from the /broadcasting/auth route, no matter what I do to try to authorize the action (up to and including using a simple return true statement). Can anyone tell me what I am doing wrong?

App/Providers/BroadcastServiceProvider.php:

namespace App\Providers;

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

class BroadcastServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        Broadcast::routes();

        /*
         * Authenticate the user's personal channel...
         */
        Broadcast::channel('App.User.*', function ($user, $userId) {
            return true;
        });
    }
}

resources/assets/js/booststrap.js:

import Echo from "laravel-echo"

window.Echo = new Echo({
    broadcaster: 'pusher',
    key: 'My-Key-Here'
});

window.Echo.private('App.User.1')
    .notification((notification) => {
        console.log(notification.type);
    });

I can see the event and it's payload in my Pusher debug console, it is simply failing once it hits the auth route.

Don't Panic
  • 13,965
  • 5
  • 32
  • 51
LorienDarenya
  • 439
  • 1
  • 4
  • 8

12 Answers12

35

Error 403 /broadcasting/auth with Laravel version > 5.3 & Pusher, you need to change your code in resources/assets/js/bootstrap.js with

window.Echo = new Echo({
    broadcaster: 'pusher',
    key: 'your key',
    cluster: 'your cluster',
    encrypted: true,
    auth: {
        headers: {
            Authorization: 'Bearer ' + YourTokenLogin
        },
    },
});

And in app/Providers/BroadcastServiceProvider.php, replace

Broadcast::routes()

with

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

or

Broadcast::routes(['middleware' => ['jwt.auth']]); //if you use JWT

or

Broadcast::routes(['middleware' => ['auth:sanctum']]); //if you use Laravel 

it worked for me, and I hope it helps you.

Macdonald
  • 880
  • 7
  • 21
Alex
  • 3,646
  • 1
  • 28
  • 25
  • 19
    This is relevant ONLY if you use API tokens. – Mostafa Attia Nov 16 '18 at 18:53
  • I have faced same problem when using passport authentication, i just Authorization Bear token. now it fixed – Syamlal Dec 06 '21 at 09:06
  • 1
    Since I use Sanctum I had to use `Broadcast::routes(['middleware' => ['auth:sanctum']]);` So it really depends on the type of authentication you're using! But still a thumbs up for putting me in the right direction :) Here is some more info on the topic: https://laravel.com/docs/8.x/broadcasting#authorizing-channels – Arno van Oordt Feb 02 '22 at 16:30
14

I solve it by creating channel route.

Create your Authorizing Channels in routes->channels.php

Broadcast::channel('chatroom', function ($user) {
    return $user;
});

See Documentation : https://laravel.com/docs/5.4/broadcasting#authorizing-channels

thanks

M Arfan
  • 4,384
  • 4
  • 29
  • 46
  • The docs you link to say the channel method should include *... a callback which returns true or false ...*, so why do you `return $user`? – Don't Panic Feb 11 '20 at 12:57
  • @Don'tPanic because for presence channels you should return info about the user, not a boolean – Mikel Granero Apr 10 '20 at 00:36
  • @MikelGranero Thank you, I now see the [Authorizing Presence Channels](https://laravel.com/docs/5.4/broadcasting#joining-a-presence-channel) section describes that. OP's question is about private channels AFAICT, not presence, so I am not sure how this answer helps, but I have learnt something :-) – Don't Panic Apr 10 '20 at 00:48
8

I paired socket.io with redis and also had a problem with 403 error, even though there weren't any authentication middlewares over /broadcasting/auth route. Only after whatching laracasts lesson I figured out that just channel authorization is not enough, there always should be user and no matter how you authenticate and obtain user, using default laravel auth or some token algorithm - jwt or anything else.

Authenticated user is automatically resolved and passed as first parameter to to closures functions in routes/channels.php file, so you can check channel availability for currently logged in user enter image description here

Jakhongir
  • 586
  • 8
  • 11
  • 1
    Thank you for your answer. But what about if I use my own Auth middleware, how do I pass this authorized user to the closure function? – Armalong Sep 02 '21 at 12:33
  • The answer is not the solution. I'm using sanctum & using that in `Auth::routes()` but still getting that error – Md. A. Apu Oct 02 '21 at 13:29
4

What worked for me was to use the method private of the Laravel Echo package: https://laravel.com/docs/5.3/notifications#listening-for-notifications

Echo.private('App.User.1')
  .notification((notification) => {
  console.log(notification.type);
});
4

Check how you are authorising your channel. Depending on your setup this might help. Update your BroadcastServiceProvider with the following:

<?php

namespace App\Providers;

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

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

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

Adds in the Auth API middleware for use with Laravel Passport.

Ben F
  • 75
  • 8
2

In my case the problem was a wrong user id:

Echo.private('user.'+CURRENT_USER_ID_HERE)
Nurbol Alpysbayev
  • 19,522
  • 3
  • 54
  • 89
0

This can happen if you are no longer logged in. Make sure you are actually logged into the Laravel app and that your current session hasn't expired.

I logged back in and it worked for me.

0

In case, someone comes in the latest Laravel 5.7 with the same issues, the good solution worked for me is to check authentication in each channel before returning or on the return like below.

Broadcast::channel('user.*', function ($user) {
    return Auth::check();
});

Broadcast::channel('conversation.{id}', function ($user, $conversationId) {
    Auth::check();
    return $user->isInConversation(Conversation::find($conversationId));
});

This way it works with any channel broadcasting any event including users.

I hope it may help someone else.

Juddling
  • 4,594
  • 8
  • 34
  • 40
Jacinto Joao
  • 31
  • 1
  • 3
0

I am facing the same Error

/broadcasting/auth 403 (Forbidden)

I just Notice that my Project/routes/channels contain

Broadcast::channel('App.User.{id}', function ($user, $id) {
    return (int) $user->id === (int) $id;
});

But my User Model is placed in Project/app/Models/User.php

so I change Project/routes/channels.php to

Broadcast::channel('App.Models.User.{id}', function ($user, $id) {
    return (int) $user->id === (int) $id;
});

/broadcasting/auth 403 (Forbidden) is fixed

also if your base url is different from http://localhost/broadcasting/auth you can define authEndpoint in Echo initialization

window.Echo = new Echo({
    broadcaster: 'pusher',
    key: process.env.MIX_PUSHER_APP_KEY,
    cluster: process.env.MIX_PUSHER_APP_CLUSTER,
    forceTLS: true,
    authEndpoint : `http://localhost/project/public/broadcasting/auth`,
});

also make sure that this piece of code is uncomment in project/config/app.php

App\Providers\BroadcastServiceProvider::class

and to listen a Private Channel Event you have to Define Echo like this in Javascript

Echo.private(`App.Models.User.{{ Auth::user()->id }}`)
.listen(".Illuminate\\Notifications\\Events\\BroadcastNotificationCreated",(notification) => {
      console.log('Got event...');
    });

Your project\app\Notifications\SendNotification.php Should Look Like this

<?php

namespace App\Notifications;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;
use Illuminate\Notifications\Messages\BroadcastMessage;

class SendNotification extends Notification implements ShouldQueue
{
    use Queueable;

    /**
     * Create a new notification instance.
     *
     * @return void
     */
    public function __construct()
    {
    }

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

    public function toBroadcast($notifiable)
    {
        return new BroadcastMessage([
            'message' => 'Hola!'
        ]);
    }
}

also use Notifiable in your User Model.

In Your Controller you can call like this

$user->notify(new SendNotification());

you can debug on Pusher Dashboard Debug console below is the screenshot if I subscribe successfully. enter image description here

You can test Event using Pusher Debug Console below is the screenshot enter image description here hope this will help

Raj Omer Mustafa
  • 81
  • 1
  • 1
  • 5
0

When you try to set up private channels in my case I use JWTTokens in laravel. For doing that in /config/auth.php you need to establish the guard that we will use to get the authenticated user

    'guards' => [
    'web' => [
        'driver'   => 'session',
        'provider' => 'users',
    ],
    'api' => [
        'driver'   => 'jwt',
        'provider' => 'users',
    ],
],

In the last piece of code we put for api guard we get the authenticated user from JWT.

In BroadcastServiceProvider we define the routes

class BroadcastServiceProvider extends ServiceProvider {
/**
 * Bootstrap any application services.
 *
 * @return void
 */
public function boot()
{
    Broadcast::routes();
    require base_path('routes/channels.php');
}}

In routes/channels.php we establish the route for the private channel and WE MUST ADD THE GUARD OPTION in the declaration of the route

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

Then in the event, that we use to broadcast the message we declare that we broadcast the message over the channel declared. You should pass in the constructor of this event, the authenticated user:

class FileZipped implements ShouldBroadcast {

use Dispatchable, InteractsWithSockets, SerializesModels;

/**
 * The file instance.
 *
 * @var FilesUser
 */
public $fileUser;

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

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

Finally, in the front, I used React v17 I declare this

const options = {
    broadcaster: "pusher",
    key: "123456_key",
    cluster: "mt1",
    forceTLS: false,
    encrypted: false,
    wsHost: '127.0.0.1',
    wsPort: 6001,
    //authEndpoint is your apiUrl + /broadcasting/auth
    authEndpoint: "http://localhost:8000/broadcasting/auth",
    // As I'm using JWT tokens, I need to manually set up the headers.
    auth: {
        headers: {
            Authorization: "Bearer " + auth.accessToken,
            Accept: "application/json"
        }
    }
};

const echo = new Echo(options);

useEffect(() => {
    echo.private(`fileszipped.`+ auth.id)
        .listen('FileZipped', (e) => {
            console.log(e.fileUser.name);
            setMessage(e.fileUser.name + ' is ready for download!!');
            getAllFilesUser()
        });
}, [])

By doing this you will receive your notifications in the front using private channels. Enjoy it!!

0

For me, the 403 error was caused by empty channel_name for JWT refresh. For some reason Axios sends Content-Type: text/plain;charset=UTF-8 which resulted in empty $request->channel_name. I set the header explicitly

    window.Pusher = require('pusher-js/with-encryption');
    window.Echo = new Echo({
      broadcaster: 'pusher',
      key: key,
      host: host,
      cluster: 'us3',
      forceTLS: true,
      authorizer: (channel) => {
        return {
          authorize: (socketId, callback) => {
//HERE header needed
            const options = { headers: { 'Content-Type': 'application/json' } };
            const data = {
              socket_id: socketId,
              channel_name: channel.name,
            };
            apiChatAxios
              .post(`/broadcasting/auth`, data, options)
              .then((response) => {
                callback(false, response.data);
              })
              .catch((error) => {
                callback(true, error);
              });
          },
        };
      },
    });
``
Lukasz Madon
  • 14,664
  • 14
  • 64
  • 108
-4
-- reinstalling websockets
-- php artisan optimize:clear
John Conde
  • 217,595
  • 99
  • 455
  • 496
  • 4
    Code dumps do not make for good answers. You should explain *how* and *why* this solves their problem. I recommend reading, "[How do I write a good answer?"](//stackoverflow.com/help/how-to-answer) – John Conde Feb 15 '21 at 19:44