Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Laravel Broadcast authentication

How do I authenticate an user to a channel with a custom auth method?

For example, in my app I use a token auth (stored in the db for each user) for my API, passed via header and read by a custom middleware.

How do I control the access with these custom auth methods for channels? How would be the client and server side? I am using socket.io and the Laravel docs are quite unclear of how auth methods works for sockets.

USE CASE:

I have users stored in DB with default Laravel migration. However, how my app is just the API, it is stateless, then, rather than using remember_token to store session tokens, I use it to store an unique authentication token, generated in the login endpoint.

Every endpoint of the API, the request pass through a middleware, where the user is retrieved by the token present in the headers

$token = Request::header('X-token');

$request->user = User::findByToken($token);

return $next($request);

Now the question comes in. Laravel docs isn't much clear on how private channels works.

How would a broadcast client pass the mentioned token and get authorized to join a given channel?

like image 997
Luiz Avatar asked Nov 01 '17 14:11

Luiz


2 Answers

The Laravel documentation on broadcasting is pretty comprehensive about this, although it doesn't go into minute detail on client libraries that it doesn't support out of the box.

In this case, you're wanting the section on authorizing channels. From the docs:

In the BroadcastServiceProvider included with your Laravel application, you will see a call to the Broadcast::routes method. This method will register the /broadcasting/auth route to handle authorization requests

When defining broadcast routes, the currently authenticated user is passed automatically as a parameter. Since you're wanting to change the authentication mechanism ahead of the broadcast route definition so that the correct user is passed along, your broadcast client would pass along the token (however you like - HTTP header, URL parameter, POST data, etc.) and then you would customise the /broadcasting/auth route to parse that data and use it to authenticate the user. From there you would use the standard Broadcast::channel() definitions to ensure that the authenticated user is also an authorized user on that channel.

As per the documentation, Broadcast::routes() takes an optional array of $attributes for it to use the web middleware. From the source:

$attributes = $attributes ?: ['middleware' => ['web']];

So if you wish, you may change the middleware authentication to 'api' by passing it to the Broadcast::routes() call (or any other standard route parameters).

Since the route is already created for you, if you want to customise it at a lower level that route functions/middleware then you would need to change the auth() method on whichever BroadcastProvider you're using. More specifically, when the route is set up, it calls the authenticate() method on the BroadcastController, which in turn simply calls the auth() method on the Broadcast facade. The facade would then call whichever provider you're using - there are a few providers defined out of the box which should give you a good starting point if you want to write your own. That being said, as long as you don't have a problem with the provider itself, it's probably easier to just write your own middleware and pass that to Broadcast::routes() as an attribute if you need something particular with your authentication (if it differs from the api authentication middleware).

As an extra, since you've tagged this with socket.io, you may also want to read up on Laravel Echo. There's also a section on talking to socket.io specifically in the broadcasting introduction.

like image 175
Leith Avatar answered Oct 07 '22 23:10

Leith


On top of Leith's answer, those scratching head and wondering why creating a custom middleware and using it in BroadcastServiceProvider.php would throw back an error.

public function boot()
{
    Broadcast::routes(['middleware' => ['custom.middleware']]);
        
    require base_path('routes/channels.php');
}

Inside Broadcaster.php there is a method called retrieveUser() which should return $request->user() as a result of successful authentication from within your custom middleware.

In my case, I was trying to pass access token to API's custom middleware; and once authenticating the user, I simply passed on my original request return $next($request)

For the above code to work, I had to fetch the user model by access token and then merge it to the original request like so

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

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

return $next($request);

So now retrieveUser() method from Broadcaster.php is able to retrieve the authenticated user with return $request->user() command, and it gets passed to Broadcast::channel method as a first parameter

Broadcast::channel('private-channel-name', function ($user) {
    // if you get here, you've been authenticated (within a custom middleware)
    return true;
});
like image 43
Armand Avatar answered Oct 08 '22 00:10

Armand