Search code examples
websocketjwtnuxt.jslaravel-echopusher-js

Nuxt websockets Laravel Echo private channel auth not triggered


Im currently working on implementing websockets in my Nuxt App. I have a Laravel backend and im using Pusher and Laravel Echo. The problem is that, when trying to connect/subscribe to a private channel - as the client is authorized via the broadcast/auth endpoint the individual channel auth (channels.php) is not hit. So it is possible for a logged in user to access a private channel that they should not be able to access.

My code/configuration is as follows:

NUXT FRONTEND:

nuxt.config.js

echo: {
 broadcaster: 'pusher',
 key: process.env.MIX_PUSHER_APP_KEY,
 cluster: process.env.MIX_PUSHER_APP_CLUSTER,
 forceTLS: process.env.NODE_ENV === 'production',
 authModule: true,
 authEndpoint: `${process.env.API_URL}/broadcasting/auth`,
 connectOnLogin: true,
 disconnectOnLogout: true,
 auth: {
  headers: {
    'X-AUTH-TOKEN': process.env.API_AUTH_TOKEN
  }
 }

},

LARAVEL BACKEND:

BroadcastServiceProvider.php

public function boot()
{
    Broadcast::routes(['middleware' => [JWTAuthMiddleware::class]]);

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

AuthController.php

public function auth(Request $request): JsonResponse
{
    $pusher = new Pusher(
        config('broadcasting.connections.pusher.key'),
        config('broadcasting.connections.pusher.secret'),
        config('broadcasting.connections.pusher.app_id')
    );
    $auth = $pusher->socket_auth($request->input('channel_name'), $request->input('socket_id'));
    return ResponseHandler::json(json_decode($auth));
}

ChatMessageEvent.php

/**
 * @inheritDoc
 */
public function broadcastOn()
{
    return new PrivateChannel('chat.' . $this->chatMessage->getChatId());
}

channels.php

Broadcast::channel(
'chat.{chatId}',
function (JwtUserDTO $user, int $chatId) {
    Log::info('test');
    return false;
}

);

As you may have noticed, we use a JWT auth strategy which is stored on the client side - so we have no sessions. But as the authorization via the auth endpoint works it should be possible to guard the individual private channels via the channels.php routing ? But as i can see in the logs, it is never reached. Am i missing some configuration ? or why am i authorized solely on the auth endpoint and not also on the individual channels routes ?


Solution

  • After a lot of searching I found out that the issue was with my AuthController.php as I had implemented my own auth function - which made it work in order to authenticate the user to the private channel. Unfortunately this then resulted in not leviating the BroadcastServiceProvider. So the solution was:

    use Illuminate\Broadcasting\BroadcastController;
    
    Route::post('broadcasting/auth', [BroadcastController::class, 'authenticate'])
            ->middleware(BroadcastMiddleware::class);
    

    This will then use the Broadcast Facade and enable use of the channels.php for authenticating the user against the given channel.

    I also had to add a middleware to set the authenticated user in the Laravel session as this is needed by the ServiceProvider.

    /**
     * @param Request $request
     * @param Closure $next
     * @return mixed
     */
    public function handle(Request $request, Closure $next)
    {
        /** @var JwtUserDTO $jwt */
        $jwt = $request->get('jwt');
        // Set the user in the request to enable the auth()->user()
        $request->merge(['user' => $jwt]);
        $request->setUserResolver(function() use ($jwt) {
            return $jwt;
        });
        Auth::login($jwt);
    
        return $next($request);
    }
    

    And to do this the Model or DTO in my case had to implement the Illuminate\Contracts\Auth\Authenticatable interface. Remember to add functionality for the getAuthIdentifierName and getAuthIdentifier to return the username and user id respectivly as this is also needed if you want to play with presences channels.