From 942c302118ce13537006d6b30fc369159514519b Mon Sep 17 00:00:00 2001 From: Raj Siva-Rajah <5361908+binaryfire@users.noreply.github.com> Date: Thu, 18 Dec 2025 05:39:58 +0000 Subject: [PATCH 1/3] fix: Exclude CSRF verification from broadcast auth routes --- src/broadcasting/src/BroadcastManager.php | 21 ++++++ tests/Broadcasting/BroadcastManagerTest.php | 75 +++++++++++++++++++++ 2 files changed, 96 insertions(+) diff --git a/src/broadcasting/src/BroadcastManager.php b/src/broadcasting/src/BroadcastManager.php index 202dd29bb..6b0d7ff6a 100644 --- a/src/broadcasting/src/BroadcastManager.php +++ b/src/broadcasting/src/BroadcastManager.php @@ -24,6 +24,7 @@ use Hypervel\Bus\UniqueLock; use Hypervel\Cache\Contracts\Factory as Cache; use Hypervel\Foundation\Http\Kernel; +use Hypervel\Foundation\Http\Middleware\VerifyCsrfToken; use Hypervel\ObjectPool\Traits\HasPoolProxy; use Hypervel\Queue\Contracts\Factory as Queue; use InvalidArgumentException; @@ -76,6 +77,8 @@ public function routes(array $attributes = []): void $attributes = $attributes ?: ['middleware' => ['web']]; } + $attributes = $this->withCsrfExclusion($attributes); + $kernels = $this->app->get(ConfigInterface::class) ->get('server.kernels', []); foreach (array_keys($kernels) as $kernel) { @@ -96,6 +99,7 @@ public function routes(array $attributes = []): void public function userRoutes(?array $attributes = null): void { $attributes = $attributes ?: ['middleware' => ['web']]; + $attributes = $this->withCsrfExclusion($attributes); $this->app->get(RouterDispatcherFactory::class)->getRouter() ->addRoute( @@ -451,6 +455,23 @@ public function forgetDrivers(): static return $this; } + /** + * Add CSRF middleware exclusion to route attributes. + * + * Broadcasting auth routes receive POST requests from the Pusher/Soketi + * JavaScript client which cannot include CSRF tokens, so CSRF verification + * must be excluded from these routes. + */ + protected function withCsrfExclusion(array $attributes): array + { + $attributes['without_middleware'] = array_merge( + $attributes['without_middleware'] ?? [], + [VerifyCsrfToken::class] + ); + + return $attributes; + } + /** * Dynamically call the default driver instance. */ diff --git a/tests/Broadcasting/BroadcastManagerTest.php b/tests/Broadcasting/BroadcastManagerTest.php index a051adb68..905755caa 100644 --- a/tests/Broadcasting/BroadcastManagerTest.php +++ b/tests/Broadcasting/BroadcastManagerTest.php @@ -5,9 +5,12 @@ namespace Hypervel\Tests\Broadcasting; use Hyperf\Contract\ConfigInterface; +use Hyperf\HttpServer\Router\DispatcherFactory as RouterDispatcherFactory; +use Hypervel\Broadcasting\BroadcastController; use Hypervel\Broadcasting\BroadcastEvent; use Hypervel\Broadcasting\BroadcastManager; use Hypervel\Broadcasting\Channel; +use Hypervel\Foundation\Http\Middleware\VerifyCsrfToken; use Hypervel\Broadcasting\Contracts\Factory as BroadcastingFactoryContract; use Hypervel\Broadcasting\Contracts\ShouldBeUnique; use Hypervel\Broadcasting\Contracts\ShouldBroadcast; @@ -19,6 +22,7 @@ use Hypervel\Container\DefinitionSource; use Hypervel\Context\ApplicationContext; use Hypervel\Foundation\Application; +use Hypervel\Foundation\Http\Kernel; use Hypervel\Queue\Contracts\Factory as QueueFactoryContract; use Hypervel\Support\Facades\Broadcast; use Hypervel\Support\Facades\Bus; @@ -117,6 +121,77 @@ public function testThrowExceptionWhenUnknownStoreIsUsed() $broadcastManager->connection('alien_connection'); } + + public function testRoutesExcludeCsrfVerification() + { + $capturedAttributes = null; + + // Use stdClass mock to avoid class instantiation issues + $router = m::mock('router'); + $router->shouldReceive('addRoute') + ->once() + ->with( + ['GET', 'POST'], + '/broadcasting/auth', + [BroadcastController::class, 'authenticate'], + m::on(function ($attributes) use (&$capturedAttributes) { + $capturedAttributes = $attributes; + return true; + }) + ); + + $routerFactory = m::mock('routerFactory'); + $routerFactory->shouldReceive('getRouter') + ->with('http') + ->andReturn($router); + + $config = m::mock(ConfigInterface::class); + $config->shouldReceive('get') + ->with('server.kernels', []) + ->andReturn(['http' => []]); + + $app = m::mock(ContainerInterface::class); + $app->shouldReceive('has')->with(Kernel::class)->andReturn(true); + $app->shouldReceive('get')->with(ConfigInterface::class)->andReturn($config); + $app->shouldReceive('get')->with(RouterDispatcherFactory::class)->andReturn($routerFactory); + + $broadcastManager = new BroadcastManager($app); + $broadcastManager->routes(); + + $this->assertArrayHasKey('without_middleware', $capturedAttributes); + $this->assertContains(VerifyCsrfToken::class, $capturedAttributes['without_middleware']); + } + + public function testUserRoutesExcludeCsrfVerification() + { + $capturedAttributes = null; + + $router = m::mock('router'); + $router->shouldReceive('addRoute') + ->once() + ->with( + ['GET', 'POST'], + '/broadcasting/user-auth', + [BroadcastController::class, 'authenticateUser'], + m::on(function ($attributes) use (&$capturedAttributes) { + $capturedAttributes = $attributes; + return true; + }) + ); + + $routerFactory = m::mock('routerFactory'); + $routerFactory->shouldReceive('getRouter') + ->andReturn($router); + + $app = m::mock(ContainerInterface::class); + $app->shouldReceive('get')->with(RouterDispatcherFactory::class)->andReturn($routerFactory); + + $broadcastManager = new BroadcastManager($app); + $broadcastManager->userRoutes(); + + $this->assertArrayHasKey('without_middleware', $capturedAttributes); + $this->assertContains(VerifyCsrfToken::class, $capturedAttributes['without_middleware']); + } } class TestEvent implements ShouldBroadcast From b39bc3b2e8eb5df01ef91240a57968cd345a996e Mon Sep 17 00:00:00 2001 From: Raj Siva-Rajah <5361908+binaryfire@users.noreply.github.com> Date: Thu, 18 Dec 2025 06:05:30 +0000 Subject: [PATCH 2/3] fix: Exclude CSRF verification from broadcast auth routes --- src/broadcasting/src/BroadcastManager.php | 26 +++------- tests/Broadcasting/BroadcastManagerTest.php | 55 ++++++++------------- 2 files changed, 27 insertions(+), 54 deletions(-) diff --git a/src/broadcasting/src/BroadcastManager.php b/src/broadcasting/src/BroadcastManager.php index 6b0d7ff6a..322867ce7 100644 --- a/src/broadcasting/src/BroadcastManager.php +++ b/src/broadcasting/src/BroadcastManager.php @@ -77,7 +77,9 @@ public function routes(array $attributes = []): void $attributes = $attributes ?: ['middleware' => ['web']]; } - $attributes = $this->withCsrfExclusion($attributes); + // Exclude from CSRF verification. These routes receive POST requests from + // Pusher/Soketi JavaScript clients which cannot include CSRF tokens. + VerifyCsrfToken::except(['broadcasting/auth']); $kernels = $this->app->get(ConfigInterface::class) ->get('server.kernels', []); @@ -99,7 +101,10 @@ public function routes(array $attributes = []): void public function userRoutes(?array $attributes = null): void { $attributes = $attributes ?: ['middleware' => ['web']]; - $attributes = $this->withCsrfExclusion($attributes); + + // Exclude from CSRF verification. These routes receive POST requests from + // Pusher/Soketi JavaScript clients which cannot include CSRF tokens. + VerifyCsrfToken::except(['broadcasting/user-auth']); $this->app->get(RouterDispatcherFactory::class)->getRouter() ->addRoute( @@ -455,23 +460,6 @@ public function forgetDrivers(): static return $this; } - /** - * Add CSRF middleware exclusion to route attributes. - * - * Broadcasting auth routes receive POST requests from the Pusher/Soketi - * JavaScript client which cannot include CSRF tokens, so CSRF verification - * must be excluded from these routes. - */ - protected function withCsrfExclusion(array $attributes): array - { - $attributes['without_middleware'] = array_merge( - $attributes['without_middleware'] ?? [], - [VerifyCsrfToken::class] - ); - - return $attributes; - } - /** * Dynamically call the default driver instance. */ diff --git a/tests/Broadcasting/BroadcastManagerTest.php b/tests/Broadcasting/BroadcastManagerTest.php index 905755caa..aad799c87 100644 --- a/tests/Broadcasting/BroadcastManagerTest.php +++ b/tests/Broadcasting/BroadcastManagerTest.php @@ -6,11 +6,9 @@ use Hyperf\Contract\ConfigInterface; use Hyperf\HttpServer\Router\DispatcherFactory as RouterDispatcherFactory; -use Hypervel\Broadcasting\BroadcastController; use Hypervel\Broadcasting\BroadcastEvent; use Hypervel\Broadcasting\BroadcastManager; use Hypervel\Broadcasting\Channel; -use Hypervel\Foundation\Http\Middleware\VerifyCsrfToken; use Hypervel\Broadcasting\Contracts\Factory as BroadcastingFactoryContract; use Hypervel\Broadcasting\Contracts\ShouldBeUnique; use Hypervel\Broadcasting\Contracts\ShouldBroadcast; @@ -23,6 +21,7 @@ use Hypervel\Context\ApplicationContext; use Hypervel\Foundation\Application; use Hypervel\Foundation\Http\Kernel; +use Hypervel\Foundation\Http\Middleware\VerifyCsrfToken; use Hypervel\Queue\Contracts\Factory as QueueFactoryContract; use Hypervel\Support\Facades\Broadcast; use Hypervel\Support\Facades\Bus; @@ -65,6 +64,7 @@ protected function tearDown(): void m::close(); Facade::clearResolvedInstances(); + VerifyCsrfToken::flushState(); } public function testEventCanBeBroadcastNow() @@ -122,23 +122,10 @@ public function testThrowExceptionWhenUnknownStoreIsUsed() $broadcastManager->connection('alien_connection'); } - public function testRoutesExcludeCsrfVerification() + public function testRoutesExcludesCsrfVerification() { - $capturedAttributes = null; - - // Use stdClass mock to avoid class instantiation issues $router = m::mock('router'); - $router->shouldReceive('addRoute') - ->once() - ->with( - ['GET', 'POST'], - '/broadcasting/auth', - [BroadcastController::class, 'authenticate'], - m::on(function ($attributes) use (&$capturedAttributes) { - $capturedAttributes = $attributes; - return true; - }) - ); + $router->shouldReceive('addRoute')->once(); $routerFactory = m::mock('routerFactory'); $routerFactory->shouldReceive('getRouter') @@ -158,26 +145,19 @@ public function testRoutesExcludeCsrfVerification() $broadcastManager = new BroadcastManager($app); $broadcastManager->routes(); - $this->assertArrayHasKey('without_middleware', $capturedAttributes); - $this->assertContains(VerifyCsrfToken::class, $capturedAttributes['without_middleware']); + // Verify the broadcasting/auth path is excluded from CSRF verification + $middleware = new VerifyCsrfToken( + m::mock(ContainerInterface::class), + m::mock(ConfigInterface::class), + m::mock(\Hyperf\HttpServer\Request::class) + ); + $this->assertContains('broadcasting/auth', $middleware->getExcludedPaths()); } - public function testUserRoutesExcludeCsrfVerification() + public function testUserRoutesExcludesCsrfVerification() { - $capturedAttributes = null; - $router = m::mock('router'); - $router->shouldReceive('addRoute') - ->once() - ->with( - ['GET', 'POST'], - '/broadcasting/user-auth', - [BroadcastController::class, 'authenticateUser'], - m::on(function ($attributes) use (&$capturedAttributes) { - $capturedAttributes = $attributes; - return true; - }) - ); + $router->shouldReceive('addRoute')->once(); $routerFactory = m::mock('routerFactory'); $routerFactory->shouldReceive('getRouter') @@ -189,8 +169,13 @@ public function testUserRoutesExcludeCsrfVerification() $broadcastManager = new BroadcastManager($app); $broadcastManager->userRoutes(); - $this->assertArrayHasKey('without_middleware', $capturedAttributes); - $this->assertContains(VerifyCsrfToken::class, $capturedAttributes['without_middleware']); + // Verify the broadcasting/user-auth path is excluded from CSRF verification + $middleware = new VerifyCsrfToken( + m::mock(ContainerInterface::class), + m::mock(ConfigInterface::class), + m::mock(\Hyperf\HttpServer\Request::class) + ); + $this->assertContains('broadcasting/user-auth', $middleware->getExcludedPaths()); } } From 1cf827c9c6806e24d48c0b34c4149af561a649e2 Mon Sep 17 00:00:00 2001 From: Raj Siva-Rajah <5361908+binaryfire@users.noreply.github.com> Date: Thu, 18 Dec 2025 06:15:47 +0000 Subject: [PATCH 3/3] fix: Exclude CSRF verification from broadcast auth routes --- src/broadcasting/src/BroadcastManager.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/broadcasting/src/BroadcastManager.php b/src/broadcasting/src/BroadcastManager.php index 322867ce7..75b025ada 100644 --- a/src/broadcasting/src/BroadcastManager.php +++ b/src/broadcasting/src/BroadcastManager.php @@ -77,8 +77,8 @@ public function routes(array $attributes = []): void $attributes = $attributes ?: ['middleware' => ['web']]; } - // Exclude from CSRF verification. These routes receive POST requests from - // Pusher/Soketi JavaScript clients which cannot include CSRF tokens. + // Exclude from CSRF verification. These routes receive POST requests + // from Pusher JavaScript clients which cannot include CSRF tokens. VerifyCsrfToken::except(['broadcasting/auth']); $kernels = $this->app->get(ConfigInterface::class) @@ -102,8 +102,8 @@ public function userRoutes(?array $attributes = null): void { $attributes = $attributes ?: ['middleware' => ['web']]; - // Exclude from CSRF verification. These routes receive POST requests from - // Pusher/Soketi JavaScript clients which cannot include CSRF tokens. + // Exclude from CSRF verification. These routes receive POST requests + // from Pusher JavaScript clients which cannot include CSRF tokens. VerifyCsrfToken::except(['broadcasting/user-auth']); $this->app->get(RouterDispatcherFactory::class)->getRouter()