LorienDarenya
LorienDarenya

Reputation: 509

Laravel /broadcasting/auth Always Fails With 403 Error

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.

Upvotes: 36

Views: 50471

Answers (14)

Adding

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

to /config/auth.php, as suggested by @Marianela Diaz, made my code work.

Upvotes: 0

Hasan Mothaffar
Hasan Mothaffar

Reputation: 61

For those using Laravel 11, the BroadcastServiceProvider class isn't available anymore. In my case, adding the appropriate broadcast config in the routes/channels.php file worked.

I am using Laravel 11 and Sanctum, and this is the content of the routes/channels.php file:

use Illuminate\Support\Facades\Broadcast;

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

Upvotes: 1

Lukasz Madon
Lukasz Madon

Reputation: 14994

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);
              });
          },
        };
      },
    });
``

Upvotes: 0

Marianela Diaz
Marianela Diaz

Reputation: 99

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!!

Upvotes: 0

Alex
Alex

Reputation: 4266

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.

Upvotes: 45

Raj Omer Mustafa
Raj Omer Mustafa

Reputation: 159

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

Upvotes: 1

agamyrat_akmyradov
agamyrat_akmyradov

Reputation: 19

-- reinstalling websockets
-- php artisan optimize:clear

Upvotes: -6

Jacinto Joao
Jacinto Joao

Reputation: 31

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.

Upvotes: 0

Jakhongir
Jakhongir

Reputation: 616

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

Upvotes: 8

Ben F
Ben F

Reputation: 75

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.

Upvotes: 4

FerrisBueller
FerrisBueller

Reputation: 1

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.

Upvotes: 0

Nurbol Alpysbayev
Nurbol Alpysbayev

Reputation: 21891

In my case the problem was a wrong user id:

Echo.private('user.'+CURRENT_USER_ID_HERE)

Upvotes: 2

M Arfan
M Arfan

Reputation: 4575

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

Upvotes: 15

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);
});

Upvotes: 4

Related Questions