Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

vjupix's avatar

How to use custom guard with laravel broadcasting?

Hey guys,

I have problems to get Laravel Broadcasting up and running with a custom guard. I am using the Laravel Webosckets package. Everything is working fine except for authenticating private channels. I always get a 401 error from the auth endpoint. I am using a custom guard to authenticate users. This is working perfectly fine on 'normal' API routes.

Relevant code:

app/Providers/BroadcastServiceProvider.php

<?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:vcs']);

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

fromapp/config/auth.php

...
    'guards' => [
        'web' => [
            'driver' => 'session',
            'provider' => 'users',
        ],
        'vcs' => [
            'driver' => 'session',
            'provider' => 'clients',
        ],
    ],

    'providers' => [
        'users' => [
            'driver' => 'eloquent',
            'model' => App\Models\User::class,
        ],

        'clients' => [
            'driver' => 'eloquent',
            'model' => App\Models\Client::class,
        ],
    ],
...

This is how I make the call from the frontend using Laravel Echo:

function joinRoomChat() {
    const token = useCookie('XSRF-TOKEN').value
    const echo = new Echo({
        authEndpoint: `http://localhost/broadcasting/auth`,
        broadcaster: 'pusher',
        key: '123456',
        wsHost: 'localhost',
        wsPort: 6001,
        forceTLS: false,
        disableStats: true,
        auth: {
            headers: {'X-XSRF-TOKEN': token,
                       'Accept': 'application/json',
            },
        }
    });
    echo.join(`room.${roomUuid}`)
    .here((clients) => {
        console.log(client);
    })
    .joining((client) => {
        console.log(client);
    })
    .leaving((client) => {
        console.log(client);
    })
    .error((error) => {
        console.error(error);
    });
}

If I now watch the browser network tab I see the following: grafik.png

As you can see I make two requests:

  1. Request is to a 'normal' API endpoint which also uses the auth:vcs-Middleware.
  2. Request comes from laravel echo which tries to authenticate

If I compare the requests I can see that Laravel Echo does not include the session cookie in the request from laravel echo. So it makes sense that I get a 401 error, because the Broadcast controller looks for a session:

from /vendor/laravel/framework/src/Illuminate/Broadcasting/BroadcastController.php

<?php

namespace Illuminate\Broadcasting;

use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
use Illuminate\Support\Facades\Broadcast;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;

class BroadcastController extends Controller
{
    /**
     * Authenticate the request for channel access.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function authenticate(Request $request)
    {
        if ($request->hasSession()) {
            $request->session()->reflash();
        }

        return Broadcast::auth($request);
    }

    /**
     * Authenticate the current user.
     *
     * See: https://pusher.com/docs/channels/server_api/authenticating-users/#user-authentication.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function authenticateUser(Request $request)
    {
        if ($request->hasSession()) {
            $request->session()->reflash();
        }

        return Broadcast::resolveAuthenticatedUser($request)
                    ?? throw new AccessDeniedHttpException;
    }
}

Because Laravel echo does not send the session cookie the controller thinks the user is unauthenticated, even if he is?

So my question is? How do I need to setup Laravel Broadcasting/Echo to authenticate with a custom guard?

I am trying to get this up and running for days now and I would be really thankful for any hint.

0 likes
1 reply
vjupix's avatar

I finally got the authentication working using the web middleware and my custom auth like this:

<?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' => 'web', 'guards' => 'vcs']);

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

The problem now: The event is fired correctly, shows up in the queue console (I am using redis queues):

vendor/bin/sail artisan queue:work

   INFO  Processing jobs from the [default] queue.

  2022-11-15 13:28:15 App\Events\ClientStreamStarted .................................................... 25.50ms DONE
  2022-11-15 13:30:54 App\Events\ClientStreamStarted ..................................................... 6.87ms DONE
  2022-11-15 13:32:09 App\Events\ClientStreamStarted ..................................................... 6.10ms DONE

But never calls the listener and is never sent to the browser... The Event/Listener is registered over here:

<?php

namespace App\Providers;

use Illuminate\Auth\Events\Registered;
use Illuminate\Auth\Listeners\SendEmailVerificationNotification;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Event;
use App\Events\ClientJoinedRoom;
use App\Listeners\ClientStreamListener;

class EventServiceProvider extends ServiceProvider
{
    /**
     * The event to listener mappings for the application.
     *
     * @var array<class-string, array<int, class-string>>
     */
    protected $listen = [
        Registered::class => [
            SendEmailVerificationNotification::class,
        ],
        ClientJoinedRoom::class => [
            ClientStreamListener::class,
        ]
    ];

    /**
     * Register any events for your application.
     *
     * @return void
     */
    public function boot()
    {
        //
    }

    /**
     * Determine if events and listeners should be automatically discovered.
     *
     * @return bool
     */
    public function shouldDiscoverEvents()
    {
        return false;
    }
}

My Listener:

<?php

namespace App\Listeners;

use App\Events\ClientStreamStarted;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Support\Facades\Log;

class ClientStreamListener
{
    /**
     * Create the event listener.
     *
     * @return void
     */
    public function __construct()
    {
        //
    }

    /**
     * Handle the event.
     *
     * @param  \App\Events\ClientStreamStarted  $event
     * @return void
     */
    public function handle(ClientStreamStarted $event)
    {
        Log::info('Event.ClientStreamStarted.Listener: ' . $event);
    }
}

The event gets called like this: ClientStreamStarted::dispatch($stream, $room);

The event gets logged and everything is working fine. But the listener never gets called and the event never gets broadcasted to the client.

What the hell is going on here?

Please or to participate in this conversation.