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?
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 theBroadcast::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.
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;
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With