beyondcode / laravel-websockets

Websockets for Laravel. Done right.
https://beyondco.de/docs/laravel-websockets
MIT License
5.08k stars 628 forks source link

Getting 419 error POST http://127.0.0.1:8000/broadcasting/auth 419 #258

Closed raju12974 closed 5 years ago

raju12974 commented 5 years ago

When I cloned this repo, everything working fine. But after run npm install then it is showing that error

kryp7ik commented 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.

kryp7ik commented 5 years ago

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.

mbalandis commented 5 years ago

@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.

roelmagdaleno commented 5 years ago

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...

mbalandis commented 5 years ago

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.

roelmagdaleno commented 5 years ago

@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 commented 5 years ago

@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 :)

33sKamal commented 4 years ago

i fixed this by delaying the channel call from js to backend

skeeith commented 4 years ago

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.

goper-leo commented 4 years ago

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,
                        },
                    },
                })
anuarars commented 4 years ago

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!!

mepsd commented 4 years ago

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']]);

simonbuehler commented 4 years ago

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?

rennokki commented 4 years ago

@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.

simonbuehler commented 4 years ago

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}"];
        }
macleash90 commented 3 years ago

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

chriskirby-dev commented 2 years ago

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!