Common Problems faced while setting up private channels with laravel-echo & laravel-echo-server.

mewcrow
2020-03-17T12:06:36Z

laravel laravel-echo laravel-echo-server private channel authentication problems

| I'll start with the basics and proceed to addressing the common problems
| faced while setting up private channels with laravel-echo & laravel-echo-server.
|
| If you are getting these errors while setup; 401, 403, 419 etc, as I did in my experience.
| this gist will help you fix these errors.
|
| Although this gist addresses common problems of laravel-echo-server setup, some problems are similar with Pusher setup.
| So it might also be useful if you're having problems with setting up Pusher with Echo.
|
| I'll try to cover eveything and try to use appropriate highlighting to single out each common problem.
| I use '---' to separate each section or common problem.
|
| ---
|
| NPM Install:
| npm install dotenv --save
| npm install laravel-echo --save
| npm install laravel-echo-server --save
| npm install laravel-echo-server --save -g
|
| note that I've installed 'laravel-echo-server' globally and locally in the project.
| Global installation will allow us to use cli commands for this package, like 'laravel-echo-server init'
| Local installation because of this good practice: https://medium.com/@titasgailius/running-laravel-echo-server-the-right-way-32f52bb5b1c8
|
| ---
|
| first and foremost, make sure you have csrf meta tag in the html . Otherwise you'll get 419 error while listening to channels
|
|
|
| ---
|
| In config/app.php
| Comment out the line below
| App\Providers\BroadcastServiceProvider::class,
|
| ---
|
| In .env, you must set these variables:
|
| APP_URL=http://your_domain.com
| // common mistake: be carefull while setting the scheme 'http' or 'https' in both local and production environments.
| // this mistake can cause 403, 404 error or an error without a status
|
| APP_NAME=YourAppName
| // I'd suggest you use a clean name without underscores, hyphens, periods or any symbols.
| // errors: 401 403
|
| JWT_SECRET=random_string
| JWT_TTL=1440
| // set these only if you are using this package for jwt authentication https://github.com/tymondesigns/jwt-auth
|
| CACHE_DRIVER=redis // optional but I'd suggest redis
| SESSION_DRIVER=redis // optional but I'd suggest redis
| QUEUE_CONNECTION=redis // this refers to the config/queue.php file
| BROADCAST_DRIVER=redis
|
| Important Note:
| your Redis installation has 0 to 16 DBs
| If you plan to use redis as CACHE_DRIVER, SESSION_DRIVER and BROADCAST_DRIVER -- which I'd certainly suggest -- you must use different DB for each driver.
| It is widely reported by many laravel developers that redis pub/sub for BROADCAST_DRIVER is affected if a single redis db is used.
|
| To use different DBs make the below entries in .env, each of these we'll set in config/database.php as redis connections.
|
| REDIS_DB=0
| REDIS_CACHE_DB=1
| REDIS_BROADCAST_DB=2
| // error: events are published to channels but echo-server won't listen
|
| ---
|
| In config/database.php, at the bottom there is 'Redis Databases' section in which redis's 'default' & 'cache' connections are defined like this
|
| 'default' => [
| 'url' => env('REDIS_URL'),
| 'host' => env('REDIS_HOST', '127.0.0.1'),
| 'password' => env('REDIS_PASSWORD', null),
| 'port' => env('REDIS_PORT', 6379),
| 'database' => env('REDIS_DB', 0),
| ],
|
| 'cache' => [
| 'url' => env('REDIS_URL'),
| 'host' => env('REDIS_HOST', '127.0.0.1'),
| 'password' => env('REDIS_PASSWORD', null),
| 'port' => env('REDIS_PORT', 6379),
| 'database' => env('REDIS_CACHE_DB', 1),
| ],
|
| // add 'broadcast' db connection after 'cache' like this:
|
| 'broadcast' => [
| 'url' => env('REDIS_URL'),
| 'host' => env('REDIS_HOST', '127.0.0.1'),
| 'password' => env('REDIS_PASSWORD', null),
| 'port' => env('REDIS_PORT', 6379),
| 'database' => env('REDIS_BROADCAST_DB', 2),
| ],
|
| ---
|
| In app\Providers\BroadcastServiceProvider.php, make sure the boot function has Broadcast::routes() and requires 'routes/channels.php'
|
| public function boot()
| {
| Broadcast::routes(); // by default these routes use middleware 'auth:web'
|
| // Broadcast::routes(['middleware' => ['auth:api']]); // for auth:api
|
| // Broadcast::routes(['middleware' => ['jwt.auth']]); // for jwt package https://github.com/tymondesigns/jwt-auth
|
| require base_path('routes/channels.php');
| }
|
| ---
|
| In config/database.php, at the bottom under redis db settings if you have: redis -> options -> prefix
| Then all your channels that you brodcast on will have a prefix 'YourAppName_database_' or 'laravel_database_'
| for example: YourAppName_database_mymessageschannel
|
| 'redis' => [
|
| 'client' => env('REDIS_CLIENT', 'predis'),
|
| 'options' => [
| 'cluster' => env('REDIS_CLUSTER', 'predis'),
| 'prefix' => Str::slug(env('APP_NAME', 'laravel'), '_').'_database_',
| // You can customize the prefix however you want, but keep in mind that while listening to the channles you'll have to use this prefix
| // error: events are published to channels but echo-server won't listen
|
| ],
|
| ...
| ]
|
| ---
|
| use 'php artisan make:event EventName' to make an event that should be broadcast. The new Event will be placed in 'app\Events' directory.
|
| I used 'php artisan make:event MessageSent'
|
| then in app\Events\MessageSent
|
| <?php
|
| namespace App\Events;
|
| use App\Message;
| use Illuminate\Broadcasting\Channel;
| use Illuminate\Queue\SerializesModels;
| use Illuminate\Broadcasting\PrivateChannel;
| use Illuminate\Broadcasting\PresenceChannel;
| use Illuminate\Foundation\Events\Dispatchable;
| use Illuminate\Broadcasting\InteractsWithSockets;
| use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
|
| class MessageSent implements ShouldBroadcast
| {
| use Dispatchable, InteractsWithSockets, SerializesModels;
|
| public $message;
|
| public function __construct(Message $message)
| {
| //
| $this->message = $message;
| //
| $this->dontBroadCastToCurrentUser(); // if you don't want to broadcast to the user who generated the event.
| }
|
| public function broadcastOn()
| {
| return new PrivateChannel("room." . $this->message->room_id);
|
| // Very very Important Note: because it sucked a lot of my time
| // If you have a redis prefix setup in config/database.php, which I have discussed in the previous section,
| // you'll have to use the prefix while listening to the channel in Laravel Echo.
| // for example: if you have prefix 'YourAppName_database_', the resulting channel will be 'YourAppName_database_room.{room_id}'
| // error: 401, 403
| }
| }
|
| ---
|
| In routes/channels.php
|
| Broadcast::channel('room.{id}', function ($user, $id) {
|
| return (int) $user->room_id === (int) $id;
| },
| ['guards' => ['api']]); // you can optionally give guards as the third agument, if you are using a guard other than the 'web' guard.
|
| // the user must be logged in to authenticate private channels, laravel will automatically place $user as the current logged in user.
|
| // Important Note: Again, if there's a redis prefix setup, as I have discussed in the section above you must prefix it here
| // Broadcast::channel('YourAppName_database_room.{id}', function ($user, $id) {
| // 'YourAppName_database_' is the redis prefix.
| // error: 401, 403
|
| ---
|
| Do 'npm install --save socket.io-client'
|
| In resources\...\bootstrap.js, configure import and configure Laravel Echo for laravel-echo-server
|
| import Echo from 'laravel-echo'
| window.io = require('socket.io-client');
|
| window.Echo = new Echo({
| broadcaster: 'socket.io',
| host: window.location.hostname + ':6001'
| });
|
| ---
|
| // make echo.js in the project directory (where .env is) and copy paste below code and save
| // here we use the locally installed 'laravel-echo-server' package
| // dotenv will help you get variables from .env file so you'll have a single environment file for both laravel and laravel-echo-server
| // after you save the code, you can open a terminal in the project directory and run 'node echo' to start laravel-echo-server
|
| require('dotenv').config();
| const env = process.env;
|
| require('laravel-echo-server').run({
| authHost: env.APP_URL,
| devMode: env.APP_DEBUG,
| database: "redis",
| databaseConfig: {
| redis: {
| host: env.REDIS_HOST,
| port: env.REDIS_PORT,
| family: 4,
| db: env.REDIS_BROADCAST_DB // 'REDIS_BROADCAST_DB' is the separate db we have setup in the previous section for broadcast channels
| }
| }
| // more laravel-echo-server options can be added, see it's documentation
| });
|
| ---
|
| In your vue component, where you'd like to listen to your channel
|
| forexample: resources\js\components\ExampleComponent.vue
|
| mounted () {
|
| var that = this
|
| // if you are using jwt auth, you must set the following headers of window.Echo options to get authenticated for private channels and you must have a mechanism to get the jwt token
| // window.Echo.connector.options.auth.headers['Authorization']
| // window.Echo.options.auth
| // if you are not using jwt, skip the two lines below
|
| window.Echo.connector.options.auth.headers['Authorization'] = 'Bearer ' + jwt_token
| window.Echo.options.auth = {
| headers: {
| Authorization: 'Bearer ' + jwt_token,
| },
| },
|
| window.Echo.private(`room.${that.room.id}`) // *
| .listen('MessageSent', function (e)
| {
| // doStuff
| })
|
| // *
| // Important Note: Again, if there's a redis prefix setup, as I have discussed in the previous sections you must prefix it here
| // window.Echo.private(`YourAppName_database_room.${that.room.id}`)
| // the channel name becomes YourAppName_database_room.{room_id}
| // 'YourAppName_database_' is the redis prefix.
| // error: 401, 403
|
| }
|