Search code examples
angularlaravelsocket.iolaravel-echo

laravel-echo The POST method is not supported for this route


so I'm trying to connect to a private channel using Laravel Echo. I am able to connect to a public channel but I'm getting issues authenticating a (logged in with correct Bearer token) private channel - getting the following error from laravel-echo-server start

L A R A V E L  E C H O  S E R V E R

version 1.6.2

⚠ Starting server in DEV mode...

✔  Running at localhost on port 6001
✔  Channels are ready.
✔  Listening for http events...
✔  Listening for redis events...

Server ready!

[10:58:39 PM] - Preparing authentication request to: https://example.co.uk
[10:58:39 PM] - Sending auth request to: https://example.co.uk/api/broadcasting/auth/

⚠ [10:58:39 PM] - K1cJaXs7jWMBNpF9AAAA could not be authenticated to private-sonect_database_user.2
{"errors":{"message":"The POST method is not supported for this route. Supported methods: GET, HEAD."}}
Client can not be authenticated, got HTTP status 401

I'm confused by the error POST method is not supported because all POST, PUT, PATCH, and DELETE all work in my Laravel project. Furthermore, I am able to hit https://example.co.uk/api/broadcasting/auth/ using Postman with a POST request.

Here's a sample of php artisan route:list

| Domain | Method        | URI                                           | Name                              | Action                                                                    | Middleware   |
|        | GET|POST|HEAD | api/broadcasting/auth                         |                                   | Illuminate\Broadcasting\BroadcastController@authenticate                  | auth:api     |

PlayerEvent.php

namespace App\Events;

use App\Models\User;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;

class PlayerEvent implements ShouldBroadcast
{
    use Dispatchable, InteractsWithSockets, SerializesModels;

    public $user;

    /**
     * Create a new event instance.
     *
     * @param User $user
     * @return void
     */
    public function __construct(User $user)
    {
        $this->user = $user;
    }

    /**
     * Get the channels the event should broadcast on.
     *
     * @return \Illuminate\Broadcasting\Channel|array
     */
    public function broadcastOn()
    {
        return new PrivateChannel('user.' . $this->user->id);
    }

    /**
     * The event's broadcast name.
     *
     * @return string
     */
    public function broadcastAs()
    {
        return 'PlayerEvent';
    }

    /**
     * The event's broadcast name.
     *
     * @return array
     */
    public function broadcastWith()
    {
        return ['message' => 'Hello world'];
    }

}

BroadcaseServiceProvider.php

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

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

config/app.php


        /*
         * Application Service Providers...
         */
        App\Providers\AppServiceProvider::class,
        App\Providers\AuthServiceProvider::class,
        App\Providers\BroadcastServiceProvider::class,
        App\Providers\EventServiceProvider::class,
        App\Providers\RouteServiceProvider::class,

routes/channel.php


Broadcast::channel('user.{user}', function ($user) {
    return true;
});

laravel-echo-server.json

{
    "authHost": "https://example.co.uk",
    "authEndpoint": "/api/broadcasting/auth/",
    "clients": [
        {
            "appId": "4cb2c72910d85007",
            "key": "d3af62258887434b9900b89a937cb7f7"
        }
    ],
    "database": "redis",
    "databaseConfig": {
        "redis": {},
        "sqlite": {
            "databasePath": "/database/laravel-echo-server.sqlite"
        },
        "publishPresence": true
    },
    "devMode": true,
    "host": null,
    "port": "6001",
    "protocol": "https",
    "socketio": {},
    "secureOptions": 67108864,
    "sslCertPath": "/var/www/example.co.uk/letsencrypt/live/example.co.uk/fullchain.pem",
    "sslKeyPath": "/var/www/example.co.uk/letsencrypt/live/example.co.uk/privkey.pem",
    "sslCertChainPath": "",
    "sslPassphrase": "",
    "subscribers": {
        "http": true,
        "redis": true
    },
    "apiOriginAllow": {
        "allowCors": true,
        "allowOrigin": "http://localhost:4200/",
        "allowMethods": "GET, POST, PUT, PATCH, DELETE",
        "allowHeaders": "Origin, Content-Type, X-Auth-Token, X-Requested-With, Accept, Authorization, X-CSRF-TOKEN, X-Socket-Id"
    }
}

I'm using angular on the front end

echo.service.ts

import {Injectable} from "@angular/core";
import {environment} from "../../../environments/environment";
import Echo from "laravel-echo";
import {UserModel} from "../models/user.model";
import * as io from "socket.io-client";

@Injectable({
    providedIn: 'root'
})
export class EchoService {

    public echo: Echo;

    constructor() {
        window['io'] = io;
        this.echo = window['Echo'] = new Echo({
            broadcaster: 'socket.io',
            host: environment.websocketUrl, // https://example.co.uk:6001
            auth: {
                headers: {
                    Authorization: `Bearer ${localStorage.getItem('token')}`
                }
            }
        });
    }

    /** privateChannel('user', 'PlayerEvent', this.user); Called from elsewhere */
    privateChannel(channelName: string, eventName: string, user: UserModel) {
        console.log('privateChannel');
        this.echo.private( environment.broadcastChannelPrefix + channelName + '.' + user.id)
            .listen('.' + eventName, data => {
                console.log(data);
            });
    }
}

Laravel: 7.12.0
laravel-echo: ^1.8.0
socket.io-client: ^2.3.0
Angular: 9.1.9


Solution

  • So I figured it. Bit of an obvious one when I realised... It's to do with my development environment - I have a virtual machine running where I've edited my /etc/hosts/ file to point example.co.uk to. Since laravel-echo authenticates the user from the server, example.co.uk now points to my live server where that route isn't properly setup.

    Changing my /etc/hosts file on the virtual machine to point to:

    192.168.33.10     example.co.uk
    

    Fixed the issue