Laravel /broadcasting/auth Always Fails With 403 Error
Asked Answered
T

13

35

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.

Ticker answered 18/1, 2017 at 20:33 Comment(0)
F
43

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.

Footie answered 18/5, 2018 at 7:14 Comment(4)
This is relevant ONLY if you use API tokens.Monkhmer
I have faced same problem when using passport authentication, i just Authorization Bear token. now it fixedLeverage
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: laravel.com/docs/8.x/broadcasting#authorizing-channelsHers
Beware! The above is not the answer of the questionPedagogy
O
15

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

Ornas answered 15/5, 2017 at 6:50 Comment(3)
The docs you link to say the channel method should include ... a callback which returns true or false ..., so why do you return $user?Tragedienne
@Don'tPanic because for presence channels you should return info about the user, not a booleanLandonlandor
@MikelGranero Thank you, I now see the Authorizing Presence Channels 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 :-)Tragedienne
G
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

Georgeta answered 4/9, 2019 at 7:42 Comment(2)
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?Improvvisatore
The answer is not the solution. I'm using sanctum & using that in Auth::routes() but still getting that errorSwaziland
P
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);
});
Protagonist answered 22/2, 2017 at 17:40 Comment(0)
L
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.

Longheaded answered 17/11, 2018 at 13:40 Comment(1)
This is giving me a 302 (Found) status :/Landonlandor
J
2

In my case the problem was a wrong user id:

Echo.private('user.'+CURRENT_USER_ID_HERE)
Jolee answered 14/2, 2018 at 12:20 Comment(1)
You save me :). Thanks.Drury
A
1

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

Aerophyte answered 10/6, 2022 at 13:17 Comment(1)
This works for me! I am using inertia + vuejs, and using notification + broadcast. Not events. Thanks!!Zosima
O
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.

Orsino answered 27/2, 2018 at 8:2 Comment(0)
A
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.

Ambroid answered 7/10, 2018 at 10:6 Comment(0)
E
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!!

Equality answered 27/8, 2022 at 13:39 Comment(0)
I
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);
              });
          },
        };
      },
    });
``
Interstellar answered 28/9, 2022 at 12:48 Comment(0)
I
0

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']);
Isiahisiahi answered 18/6 at 10:50 Comment(0)
U
-5
-- reinstalling websockets
-- php artisan optimize:clear
Unbelief answered 15/2, 2021 at 18:43 Comment(1)
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?"Expunge

© 2022 - 2024 — McMap. All rights reserved.