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.