7

I am using Laravel Event Broadcast and Pusher to utilize websockets both on my API and Web. If I try them individually, both work fine. What I mean is:

Broadcast::routes(['middleware' => 'web']); // this works for my Laravel website

Broadcast::routes(['middleware' => 'auth:api']); // this works for my api

However, if I want to use both at the same time like this:

Broadcast::routes(['middleware' => ['auth:api', 'web']]); // doesn't work

... it crashes for both, which I suspect that it is assuming I am trying to enable for both auth:api && web middlewares.

Is there a way to use an OR kind of statement for this (auth::api || 'web')? What if I want to use both at the same time and if it passes one middleware, it bypasses the middleware.

Please note that I am using Laravel Passport for my api.


Or is there a way to combine and creating a mixed middleware for both (which will essentially check for either api or web)? So I can use something like this maybe:

Broadcast::routes(['middleware' => 'broadcast']); // or auth:broadcast

Update:

As far as I understand, if I create a new Middleware called broadcast, I can do:

class BroadcastMiddleware() {

  public function handle() {
    $web = Auth::guard('web')->user();
    if ($web) {
        return response()->json($web);
    }

    $api = Auth::guard('api')->user();
    if ($api) {
        return response()->json($api);
    }
    return response()->json('Unauthorized.', 500);
  }
}

But then how do I change /broadcasting/auth route? If I try this:

Route::post('/realtime/auth', function(){
    return true;
})->middleware('broadcast');

This returns the user object info, however instead, it should return something like: auth:"374f7ff42b7da877aa35:c29addedec281b418331a61dc3cfc74da8b108222565fa4924a8..."

Cœur
  • 37,241
  • 25
  • 195
  • 267
senty
  • 12,385
  • 28
  • 130
  • 260

5 Answers5

12

Why not just use something like this in the BroadcastServiceProvider? This creates two separate endpoints with separate middleware assigned.

    Broadcast::routes(['middleware' => 'web']);

    Broadcast::routes(['prefix' => 'api', 'middleware' => 'api']);
Alex Lacayo
  • 1,462
  • 1
  • 19
  • 27
4

I finally figured out how to do it.

I am not sure if it is the best way of achieving this, and I'd highly appreciate any improvements.

How I achieved is created a new middleware for 'web' and left the other one as it it. Here are the steps.

1) In 'BroadcastServiceProvider', left only auth:api guard for Broadcast::routes(['middleware' => 'auth:api']);.

This way, Laravel's auth:api method for authenticating broadcasting works as expected.

2) Created a middleware called "Broadcast" and mapped it in Kernel.php like so:

'broadcast' => \App\Http\Middleware\Broadcast::class

and the Broadcast.php middleware looks like this:

public function handle($request, Closure $next)
{
    $web = Auth::guard('web')->user();
    if ($web) {
        return response()->json(\Illuminate\Support\Facades\Broadcast::auth($request));
    }

    return response()->json('Unauthorized.', 500);
}

3) Created a unique route other than Laravel's /broadcasting/auth in my routes>web.php

Route::post('/guard/broadcast/auth', function(\Illuminate\Support\Facades\Request $req){
    return true;
})->middleware('broadcast');

4) And then only on my blade, I use it like so:

<script>

let pusher = new Pusher("{{ env('PUSHER_APP_KEY') }}", {
    cluster: 'us2',
    encrypted: true,
    auth: {
        headers: {
            'X-CSRF-TOKEN': "{{ csrf_token() }}"
        }
    },
    authEndpoint: '{{ env('APP_URL') }}' + '/guard/broadcast/auth',
});

let channel = pusher.subscribe('private-channel.{{ Auth::user()->id }}');

channel.bind('my-event', addMessage);

function addMessage(data) {
    console.log(data);
}

</script>
senty
  • 12,385
  • 28
  • 130
  • 260
1

I'm preferable just using middleware that extends to both auth:api and web middlewares.

like what I posted in here: https://github.com/tlaverdure/laravel-echo-server/issues/266#issuecomment-365599129. So, I just maintenance 1 middleware if I wanted to change it in the future

  • But, this approach didn't return me `auth:"374f7ff42b7da877aa35:c29addedec281b418331a61dc3cfc74da8b108222565fa4924a8..."` which is what the authenticator needs – senty Feb 24 '18 at 16:48
0

BroadcastServiceProvider

if (request()->hasHeader('authorization')){
    Broadcast::routes(['middleware' => 'auth:api']);
} else {
    Broadcast::routes();
}
René Höhle
  • 26,716
  • 22
  • 73
  • 82
gohari7
  • 1
  • 1
0

It is better to use prefix approach for achieve multiple authorization types. If you will use a middleware - it is just redundant middleware. If you will use if block (as on a code snippet below): you will face problem with routes caching, it will return 403 error becuase Laravel should cache a route with a set of middlewares.

if (request()->hasHeader('authorization')){
    Broadcast::routes(['middleware' => 'auth:api']);
} else {
    Broadcast::routes();
}

You even may register separate service providers for web and api to split responsibilities and it will work. For Web

class BroadcastServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        Broadcast::routes(['middleware' => ['web']);

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

And for Api

class ApiBroadcastServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        Broadcast::routes(['prefix' => 'api', 'middleware' => ['auth:api']]);

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