0
votes

I know I'm not the first struggling with this. But after some days and going trough a lot of related questions i somehow feel that my case deserves it's own question :).

I have a working websocket solutions with Laravel Websockets (https://beyondco.de/docs/laravel-websockets/getting-started/introduction) and Laravel Echo for public channels. My client application is a vue-cli app and connects to the server + broadcast messages on public channels work great. The authorization is handled by Laravel Passport. So through sending a Bearer token in the Authorization header the backend application knows if the user is authenticated.

However I'm struggling to get Private channels to work. Trying to authenticate always gives me this error:

Access to XMLHttpRequest at 'https://my-app.test/broadcasting/auth' from origin 'https://localhost:8080' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.

I know this CORS error comes on my way when I have a server issues so I tried debugging the request in Insomnia. However when mimicking the request in Insomnia it gives a response 200 and also what would be expected:

Mimick auth request in Insomnia

I've been reading several guides and stackoverflow questions but I can't find anything similar. Going back to it might be a CORS issues but I don't think that is the case. My OPTIONS request returns back just fine.

To be complete I also add some code that might help in debugging.

My BroadcastServiceProvider

public function boot()
{
    Broadcast::routes(['middleware' => ['auth:api']]);

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

My channels.php

use Illuminate\Support\Facades\Broadcast;

Broadcast::channel('App.User.{id}', function ($user, $id) {
    return (int) $user->id === (int) $id;
});

The client

this.echoClient = new Echo({
  broadcaster: 'pusher',
  key: process.env.VUE_APP_WEBSOCKETS_APP_ID,
  wsHost: process.env.VUE_APP_WEBSOCKETS_URL,
  wssPort: 6001,
  // forceTLS: true,
  disableStats: true,
  authEndpoint: process.env.VUE_APP_SERVER_URL + '/broadcasting/auth',
  auth: {
    headers: {
      Authorization: "Bearer " + this.$store.state.auth.auth_token
    }
  }
})

// this one works!!
this.echoClient.channel('App.User')
  .listen('UpdatePosts', (e) => {
    console.log('event received')
    console.log(e)
  })

// private channels don't work ... :'(
this.echoClient.private('App.User.' + this.user.id)
  .listen('UpdatePosts', function(e) {
    console.log(e)
  })
2
what api your using ? laravel santum ?Kamlesh Paul
@KamleshPaul - I'm using Laravel Passport.Dante

2 Answers

2
votes

I've got the same problem By using fruitcake/laravel-cors, it was solved.

this is my auth option:

    auth : {
        headers : {
             Authorization: "Bearer " + token,
             Accept: "application/json",

        }
    },
2
votes

for anyone struggling with this issue. For me the solution was to add the api prefix to the broadcast::auth method.

public function boot()
{
    Broadcast::routes(['prefix' => 'api', 'middleware' => ['auth:api']]);

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

Offcourse you need to correctly set the api prefix on the client:

  authEndpoint: process.env.VUE_APP_SERVER_URL + '/api/broadcasting/auth',

I suppose the difference is that when you prefix api Laravel we specifically tell the server to ditch web Middleware.

I still don't really understand why the request was succesfull from Insomnia since there was no x-csrf header set. Insomnia did send a cookie header. Maybe that's the reason why it was working there.

EDIT

Solution provide by @Tippin on laracasts forum.

To add to the answer, it was a CORS issue after all.

https://github.com/fruitcake/laravel-cors

Prefixing the broadcast route with API does not alter middleware at all, so that is not putting it in the api middleware group. What I do think is happening is you may have the cors package installed and in the allowed paths, you have something like api/*, so by simply adding that prefix, you solved your issue. Otherwise, you can add the default broadcast to the whitelist (assuming you use that package for CORS):

/*
 * You can enable CORS for 1 or multiple paths.
 * Example: ['api/*']
 */
'paths' => ['api/*', 'broadcasting/auth'],

https://github.com/fruitcake/laravel-cors/blob/master/config/cors.php