Skip to content

Conversation

@binaryfire
Copy link
Contributor

Problem

When using pooled broadcast drivers (Pusher, Ably), channel authorization fails with 403 errors because channels registered via Broadcast::channel() are proxied through __call() to individual pooled driver instances.

In a coroutine environment:

  1. Broadcast::channel('App.Models.User.{id}', ...) is called at boot time → registers on pooled instance A
  2. Auth request comes in → Broadcast::auth() proxied to pooled instance B (empty channels array)
  3. Result: 403 AccessDeniedHttpException because no channel patterns match

Solution

Store channels directly on the singleton BroadcastManager instead of proxying to pooled drivers:

  • Add $channels and $channelOptions arrays to BroadcastManager
  • Add explicit channel() method that stores channels on the manager
  • Add explicit auth() method that performs authorization using the manager's channels array
  • Only delegate signature generation (validAuthenticationResponse()) to the driver

This ensures all channel registrations are shared across all pooled broadcaster instances while maintaining the performance benefits of connection pooling.

Changes

  • Added $channels and $channelOptions properties
  • Added channel(), auth(), getChannels() methods
  • Added helper methods for authorization logic (pattern matching, parameter extraction, implicit model binding, user retrieval)
  • Added test testChannelsAreStoredOnManagerNotProxiedToDriver

Breaking Changes

None. All existing methods and behavior are preserved. The fix only affects how channels are stored internally.

@albertcht
Copy link
Member

Hi @binaryfire , thank you for reporting this issue. I'm thinking, compared to moving the channel logic to BroadcastManager, would it be simpler to make the channel-related properties in Broadcaster static?

By making these properties static:

protected static $channels = [];
protected static $channelOptions = [];

This way, besides avoiding method overlap with the original broadcaster methods, we can also maintain the original functional definition of the broadcaster as much as possible. The authorization logic (auth(), resolveAuthenticatedUser(), etc.) remains in the Broadcaster class where it belongs, and BroadcastManager stays focused on driver management.

Additionally, we could add a new flush method in the broadcaster to conveniently clear the static properties for testing purposes:

public static function flushChannels(): void
{
    static::$channels = [];
    static::$channelOptions = [];
}

What do you think?

@albertcht albertcht added the bug Something isn't working label Dec 6, 2025
@binaryfire
Copy link
Contributor Author

@albertcht That's much better. Don't know why I didn't think of that - it's so obvious. I've made the changes and added a test. The tests pass locally, but it looks like there was an error in the CI pipeline. Could you have a look and see what the problem is?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants