Closed raju12974 closed 5 years ago
I'm having the same issue. I'm using this package in a few production environments with no issue but I just re-installed my OS on my dev machine and after setting up the project I've been working on I'm receiving the same error. Currently installed version is 1.3 which is also the same on my other environments that are working so perhaps I'm just being an idiot but figured I'd add a comment to note raju12974 isn't the only one with this issue. The one difference with my setup compared to OP is I'm running an Nginx reverse proxy to access the socket server.
Ok I have confirmed it's something to do with the newest version of laravel-echo. Temporary work around is go to your package.json file and change this "laravel-echo": "^1.5.4", to this "laravel-echo": "1.5.4". Then run npm update to install version 1.5.4 and recompile your JS files. Something in 1.6 broke it.
@kryp7ik @raju12974
Nothing to do with this package but...
I had 419 error. It was because due to a csrf token not being added in the response. I didn't figure out why but. You probably can fix it by adding in VerifyCsrfToken middleware:
protected $except = [
'/broadcasting/auth',
];
Also right above it I also have:
protected $addHttpCookie = true;
This is default in laravel/laravel package. Not sure if this does anything to it as well.
After adding so I seen the token in response and got 403 error which i fixed in routes/channels.php
It only affected private channels and this is how i solved it. It may or may not work for you.
I had the error too in Private and Presence channels, it happened on my project and Laravel WebSockets demo too.
I can confirm that @kryp7ik solution worked by downgrading the "laravel echo" package to 1.5.4, also @mbalandis solution worked too but I have no idea how dangerous would be by bypassing the CSRF Token verification to the broadcasting auth route.
Well, looks like Laravel Echo is not grabbing the CSRF Token for some reason, but if you read connector.ts
file from the package you will find this code:
protected csrfToken(): string {
let selector;
if (typeof window !== 'undefined' && window['Laravel'] && window['Laravel'].csrfToken) {
return window['Laravel'].csrfToken;
} else if (this.options.csrfToken) {
return this.options.csrfToken;
} else if (
typeof document !== 'undefined' &&
document.hasOwnProperty('querySelector') &&
(selector = document.querySelector('meta[name="csrf-token"]'))
) {
return selector.getAttribute('content');
}
return null;
}
The first condition is looking for the window.Laravel.csrfToken
variable so if you set it in your layout head it fixes the issue without downgrading the package or bypassing the token verification:
<script>
window.Laravel = {
csrfToken: "{{ csrf_token() }}"
};
</script>
After set that global variable the broadcast auth should return a 200 HTTP response. But now I have my doubts why Laravel Echo is not grabbing the meta token HTML field...
@roelmagdaleno Yes I agree it can be dangerous so I kept searching I figured it out. And yes, echo is not grabbing but I don't think it is the fault of echo but pusher because same pattern fixes the problem. See code below is the solution :) I don't think it was the case in previous versions so they broke something..
Just add auth like so:
window.Echo = new Echo({
broadcaster: "pusher",
key: pusher_app_key,
cluster: pusher_cluster,
encrypted: pusher_encrypted,
auth: {
headers: {
'X-CSRF-TOKEN': <token string here>,
},
},
})
For pusher directly same thing but different you need to do auth endpoint as well like this:
window.pusher = new Pusher(pusher_app_key, {
cluster: pusher_cluster,
forceTLS: pusher_encrypted,
authEndpoint: '/pusher_auth.php',
auth: {
headers: {
'X-CSRF-TOKEN': <token string here>,
},
},
})
Therefor you can remove '/broadcasting/auth',
from middleware.
I am 100% certain this works. If it doesn't just send me a snippet of your code I'll have a look.
@mbalandis Thank you, I ended up using my own solution by adding the CSRF Token to Laravel global variable, like I said in my previous comment:
<script>
window.Laravel = {
csrfToken: "{{ csrf_token() }}"
};
</script>
I prefer this way so I can use it later somewhere else but it is one of many fixes. Glad I could help!
@mbalandis Thank you, I ended up using my own solution by adding the CSRF Token to Laravel global variable, like I said in my previous comment:
<script> window.Laravel = { csrfToken: "{{ csrf_token() }}" }; </script>
I prefer this way so I can use it later somewhere else but it is one of many fixes. Glad I could help!
I didn't even know you can do this way. Good to know. Thanks :)
i fixed this by delaying the channel call from js to backend
guys how do you actually do this if you're connecting on Android or Flutter? I'm having CSRF token issue and I don't know how to get the token on API.
If your'e using Laravel Sanctum
add this to BroadcastServiceProvider
Broadcast::routes(['middleware' => 'auth:sanctum'])
and
new Echo({
broadcaster: 'pusher',
key: process.env.MIX_PUSHER_APP_KEY,
wsHost: '127.0.0.1',
wsPort: 6001,
disableStats: true,
auth: {
headers: {
Authorization: 'Bearer ' + token_here,
},
},
})
SOLUTION
@roelmagdaleno Yes I agree it can be dangerous so I kept searching I figured it out. And yes, echo is not grabbing but I don't think it is the fault of echo but pusher because same pattern fixes the problem. See code below is the solution :) I don't think it was the case in previous versions so they broke something..
Just add auth like so:
window.Echo = new Echo({ broadcaster: "pusher", key: pusher_app_key, cluster: pusher_cluster, encrypted: pusher_encrypted, auth: { headers: { 'X-CSRF-TOKEN': <token string here>, }, }, })
For pusher directly same thing but different you need to do auth endpoint as well like this:
window.pusher = new Pusher(pusher_app_key, { cluster: pusher_cluster, forceTLS: pusher_encrypted, authEndpoint: '/pusher_auth.php', auth: { headers: { 'X-CSRF-TOKEN': <token string here>, }, }, })
Therefor you can remove
'/broadcasting/auth',
from middleware.I am 100% certain this works. If it doesn't just send me a snippet of your code I'll have a look.
Thanks Dude!!
Just add Broadcast middleware Default Broadcast::routes() if you are using sanctum or other api than replace with Broadcast::routes(['middleware' => ['auth:api']]); if you are using JWT than replace with Broadcast::routes(['middleware' => ['jwt.auth']]);
guys how do you actually do this if you're connecting on Android or Flutter? I'm having CSRF token issue and I don't know how to get the token on API.
trying to connect from a .net client to the server but also am failing with the CSRF token, have you been successful with fixing this?
@simonbuehler You can install Sanctum and focus on the details given for SPA apps: https://laravel.com/docs/8.x/sanctum#spa-authentication
If you make it all right by calling the csrf endpoint and use sanctum guard on the Broadcasting routes like @mepsd explained, it should not throw 419 anymore.
just as a note for someone who might need it:
fixed the c# pusher client Auth Handling like this
public string Authorize(string channelName, string socketId)
{
if (authTokens.ContainsKey(channelName + socketId))
{
return authTokens[$"{channelName}{socketId}"];
}
CookieContainer cookies = new CookieContainer();
HttpClientHandler handler = new HttpClientHandler();
handler.CookieContainer = cookies;
using (var httpClient = new HttpClient(handler) )
{
HttpResponseMessage response;
Cookie cookie = new Cookie();
response = httpClient.GetAsync("http://websockets.test/sanctum/csrf-cookie").Result;
cookie = cookies.GetCookies(new Uri("http://websockets.test/sanctum/csrf-cookie")).Cast<Cookie>().FirstOrDefault(x => x.Name == "XSRF-TOKEN");
string XSRFToken = cookie.Value.Replace("%3D", ""); // Fix additional "=" sended , breaking decryption
var data = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("channel_name", $"{channelName}"),
new KeyValuePair<string, string>("socket_id", $"{socketId}")
};
HttpContent content = new FormUrlEncodedContent(data);
content.Headers.Add("X-XSRF-TOKEN", XSRFToken);
content.Headers.Add("X-APP-ID", "myid");
response = httpClient.PostAsync(_authEndpoint, content).Result;
authTokens.Add($"{channelName}{socketId}", response.Content.ReadAsStringAsync().Result);
}
return authTokens[$"{channelName}{socketId}"];
}
The solution to this problem that I just discovered is this...
In your base layout you will need to add a meta tag that contains the csrf token that laravel will use in making requests to /broadcasting/auth
In my case, my base layout was /resources/views/layouts/app.blade.php
I added this line of code within <head> ... </head>
<meta name="csrf-token" content="{{ csrf_token() }}" />
None of the solutions worked for me except that
If you don't know what your base layout is, simply put <meta name="csrf-token" content="{{ csrf_token() }}" />
into the .blade.php file of the page you would like to use the websockets
I had a 419 on anything but a public channel, found the reason to be... Echo options: headers 'X-App-ID' and 'X-CSRF-Token' setting the X-CSRF-Token' on the options somehow creates as ( appended ?? repeated ) CSRF, CSRF string instead of intended (I Think) behavior's replacing the header value. Removing the 'X-CSRF-Token' option fixed it for me.
const options = {
broadcaster: 'pusher',
....
auth: {
headers: {
'X-App-ID': APP_ID,
'X-CSRF-Token': CSRF_TOK
}
}
};
Resulted In 419
const options = {
broadcaster: 'pusher',
....
auth: {
headers: {
'X-App-ID': APP_ID
}
}
};
Success!
When I cloned this repo, everything working fine. But after run npm install then it is showing that error