0

I am deploying laravel websockets in a docker environment with JWT authentication, but I am encountering some problems. Here is the scenario:

Docker compose:

websocket:
    container_name: websocket
    build:
      args: 
        user: root
        uid: 1000
      context: ./
      dockerfile: Dockerfile
    restart: unless-stopped
    command: "php artisan websockets:serve --host=0.0.0.0"
    ports:
      - ${WEBSOCKET_PORT}:${WEBSOCKET_PORT}
    expose:
      - ${WEBSOCKET_PORT}
    volumes:
      - ./:/var/www
    depends_on:
      - redis
    networks:
      - network

When I run this container, I get the following output in the log: Starting the WebSocket server on port 6001...

broadcasting.php:

'pusher' => [
            'driver' => 'pusher',
            'key' => env('PUSHER_APP_KEY'),
            'secret' => env('PUSHER_APP_SECRET'),
            'app_id' => env('PUSHER_APP_ID'),
            'options' => [
                'host' => env('PUSHER_HOST'),
                'port' => env('PUSHER_PORT'),
                'scheme' => env('PUSHER_SCHEME'),
                'encrypted' => env('PUSHER_SCHEME') === 'https',
                'useTLS' => env('PUSHER_SCHEME') === 'https',
            ],
        ],

websockets.php:

 'dashboard' => [
        'port' => env('LARAVEL_WEBSOCKETS_PORT', 6001),
    ],
    'apps' => [
        [
            'id' => env('PUSHER_APP_ID'),
            'name' => env('APP_NAME'),
            'key' => env('PUSHER_APP_KEY'),
            'secret' => env('PUSHER_APP_SECRET'),
            'enable_client_messages' => false,
            'enable_statistics' => true,
        ],
    ],

.env (example):

WEBSOCKET_PORT=6001
PUSHER_HOST=websocket
PUSHER_PORT=6001
PUSHER_SCHEME=http
PUSHER_APP_ID=websocket
PUSHER_APP_KEY=6MFiekz7JdCjDaPEGxvRnHVCgy2Wz5Sg8OwvWitatA7JdWDtWhZl6KWcBapoukcN
PUSHER_APP_SECRET=7ZmkuySnepbe4pLRS8Ej96J58CrHzyMrPTHFKY92VSh3t831LymV7nvD6T2XNmC8
PUSHER_APP_CLUSTER=mt1

The system uses two types of profiles for authentication. Here are the auth config:

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

I uncommented the class App\Providers\BroadcastServiceProvider::class in app.php

BroadcastServiceProvider.php:

class BroadcastServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap any application services.
     */
    public function boot(): void
    {
        Broadcast::routes([
            'prefix' => 'api/v1',
            'middleware' => [
                'set.auth.user.guard:users_service_provider',
                'jwt.verify'
            ]
        ]); 

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

SetAuthUserGuardProviderMiddleware.php:

public function handle(Request $request, Closure $next, ...$guardNames): Response
    {
        $guardProvider = null;

        if(empty($guardNames)) {
            return $this->responseUnauthorized('No user profile defined for this resource');
        }

        if(!$header = $request->header('Authorization')) {
            return $this->responseUnauthorized('Authorization Token not found');
        }

        if ($header) {
            $token = str_replace('Bearer ', '', $header);
            $payload = json_decode(base64_decode(str_replace('_', '/', str_replace('-', '+', explode('.', $token)[1]))), true);
            if(!isset($payload['guard'])) {
                return $this->responseUnauthorized('The given token has no user profile defined');
            }

            $guardProvider = $payload['guard'];
        }
        
        if (in_array($guardProvider, $guardNames)) {
            auth()->shouldUse($guardProvider);
        } else {
            return $this->responseUnauthorized('The given token is not authorized to access this resource');
        }

        return $next($request);
    }

JwtMiddleware.php:

public function handle(Request $request, Closure $next)
    {
        try {
            $user = JWTAuth::parseToken()->authenticate();

        } catch (\Exception $e) {
            if ($e instanceof \Tymon\JWTAuth\Exceptions\TokenInvalidException){
                return $this->responseUnauthorized('Token is Invalid');

            } else if ($e instanceof \Tymon\JWTAuth\Exceptions\TokenExpiredException) {
                return $this->responseUnauthorized('Token is Expired');

            } else if ($e instanceof \Tymon\JWTAuth\Exceptions\TokenBlacklistedException) {
                return $this->responseUnauthorized('The token has been blacklisted');

            } else {
                return $this->responseUnauthorized('Authorization Token not found');
            }
        }
        return $next($request);
    }

channels.php:

Broadcast::channel('SendNewServicesForProvider.{id}', function (ServiceProviderUser $user, $id) {
    return true;
}, ['guards' => ['users_service_provider']]);

But when I access the Websockets Dashboard and try to authenticate, I get the following message: TypeError: method_exists(): Argument #1 ($object_or_class) must be of type object|string, null given in file /var/www/vendor/laravel/framework/src/Illuminate/Broadcasting/Broadcasters/PusherBroadcaster.php on line 112

When I do this I get the following log in the container:

New connection opened for app key 6MFiekz7JdCjDaPEGxvRnHVCgy2Wz5Sg8OwvWitatA7JdWDtWhZl6KWcBapoukcN.
Connection id 712188823.393438175 sending message {"event":"pusher:connection_established","data":"{\"socket_id\":\"712188823.393438175\",\"activity_timeout\":30}"}

Versions:

Laravel version: ^10.0
beyondcode/laravel-websockets: ^1.12
pusher/pusher-php-server: ^1.5

Thanks :)

I already tried changing all the settings for creating docker-compose and using other websockets packages I would like to be able to authenticate myself to the websockets server and be able to trigger events in real time.

0 Answers0