Forked from 1mursaleen/laravel laravel-echo laravel-echo-server private channel authentication problems
Created
August 27, 2021 07:02
-
-
Save sidigi/ee16957c362b77142023023be77d76ef to your computer and use it in GitHub Desktop.
Common Problems faced while setting up private channels with laravel-echo & laravel-echo-server.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 <head>. Otherwise you'll get 419 error while listening to channels | |
<meta content="{{ csrf_token() }}" name="csrf-token"> | |
--- | |
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 | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment