How to change Laravel's default broadcast auth middleware

Viewed 4189

So, as my title says, I want to change Laravel's default Broadcast auth middleware to a custom auth middleware that I made which uses token-based authentication. I made this because my app is an API-based app, and, when a user authenticates, I create a session token and send it to him and also store it inside the DB with an expires_at column. I am using Pusher.

I have the following middleware:

class AuthCustom
{
    public function handle($request, Closure $next)
    {
        // if we have the session token stored in header
        if ($request->header('x-session')) {
            $session = Session::where('id', $request->header('x-session'))->where('expires_on', '>=', date('Y-m-d G:i:s'))->with('user')->first();
            if ($session !== null) {
                $user = (new User())->where('id', $session->user_id)->first();
                if ($user !== null) {
                    $request->merge(['user' => $user]);

                    return $next($request);
                }
            }
        }
}

My BroadcastServiceProvider code is as follows:

class BroadcastServiceProvider extends ServiceProvider
{
    public function boot()
    {
        Broadcast::routes();

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

If I put Broadcast::routes(['middleware' => 'authcustom']); in BroadcastServiceProvider, the boradcasting/auth gives a 403 status code because $request->user() is null, which then results in an Access forbidden.

I have tried searching the whole damn web, and I found nothing about changing the default auth middleware for broadcasting.

I even tried removing Broadcast::routes() and customizing a new route /broadcast which returned a Pusher socket_auth object and everytime I got a 419 Unkown status code.

Any ideas or maybe you can point me in the direction where I could manage this? Thank you!

Later edit: My JS Echo connection looks like this:

Vue.use(VueEcho, {
    broadcaster: 'pusher',
    key: 'xxxxxxxxxxxxxx',
    cluster: 'eu',
    authEndpoint: 'http://localhost/api.easycargo.ro/public/broadcasting/auth',
    auth: {
        headers: {
            'x-session': this.auth.token
        }
    }
});
2 Answers

I'm glad you got something working. For later readers, here's a more Laravel-esque way to solve the problem in the question: create a custom auth guard used to authenticate requests for the special routes.

Laravel's AuthManager includes a helper method—viaRequest()—that simplifies the creation of a Guard that authenticates a user with data from the request context without the need to fully-implement Illuminate\Contracts\Auth\Guard. We can bind our custom guard in the boot() method in AuthServiceProvider.php:

public function boot()
{
    Auth::viaRequest('custom-auth', function ($request) {
        // Any custom user-lookup logic here. For example:
        if ($request->header('x-session')) {
            $user = // ...try to fetch a user...
            return $user;
        }
    });
}

As we can see, we just pass a closure to the viaRequest() method that returns a User object when authentication succeeds, or null when authentication fails.

Next, we'll tell Laravel about our new auth guard by adding an entry to the 'guards' array in config/auth.php:

'guards' => [ 
    ...
    'broadcasting' => [
        'driver' => 'custom-auth', 
    ],
],

Finally, we need to update the middleware for any routes that should authenticate a user with our custom Guard. We can use Laravel's built-in auth middleware and specify which guard to apply as a middleware parameter. For example, we'll initialize the broadcasting routes in the question's BroadcastServiceProvider.php:

Broadcast::routes([ 'middleware' => [ 'auth:broadcasting', ... ] ]);

...where broadcasting matches the name we assigned to our custom Guard in config/auth.php.

This approach enables us to use all of Laravel's Auth services, provides a more central place to define our authentication logic, and simplifes automated testing because we can more easily mock up authentication as needed.

I actually managed to find a solution, so all I needed to do was bind the $user that I got in my custom auth middleware to the request by doing the following thing:

$request->merge(['user' => $user]);

//add this
$request->setUserResolver(function () use ($user) {
   return $user;
});

and now $request->user() which laravel checks returns the user object and passes the validation.

Related