You can create your own auth guard and it's also pretty simple but more complex.
- Create a class which will implement Authenticable Interface.
- Create UserProvider.
- Create a new Guard.
- Register Guard and UserProvider in AuthServiceProvider.
- Add provider and guard in config/auth.php
- Use your new guard.
Advantages
- You don't have to modify auth endpoint
- You don't have to change default guard
- You base on Laravel Auth system
- Keep support of multiple tabs in the browser
- Can be used with web guard at the same time
- Keep all the advantages of using PresenceChannel
Disadvantages
So,
1. Create a new class which will implement Authenticable interface.
<?php
namespace App\Models;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Contracts\Support\Arrayable;
use Illuminate\Contracts\Support\Jsonable;
use JsonSerializable;
/**
* @property string $id
* @property string $name
*/
class Session implements Authenticatable, Jsonable, Arrayable, JsonSerializable
{
private $id;
private $attributes = [];
public function __construct($id)
{
$this->id = $id;
$this->name = "Guest";
}
/**
* Get the name of the unique identifier for the user.
*
* @return string
*/
public function getAuthIdentifierName()
{
return 'id';
}
/**
* Get the unique identifier for the user.
*
* @return mixed
*/
public function getAuthIdentifier()
{
return $this->{$this->getAuthIdentifierName()};
}
/**
* Get the password for the user.
*
* @return string
*/
public function getAuthPassword()
{
return "";
}
/**
* Get the token value for the "remember me" session.
*
* @return string
*/
public function getRememberToken()
{
return $this->{$this->getAuthIdentifierName()};
}
/**
* Set the token value for the "remember me" session.
*
* @param string $value
* @return void
*/
public function setRememberToken($value)
{
$this->{$this->getRememberToken()} = $value;
}
/**
* Get the column name for the "remember me" token.
*
* @return string
*/
public function getRememberTokenName()
{
return "token";
}
public function __get($name)
{
return $this->attributes[$name];
}
public function __set($name, $value)
{
$this->attributes[$name] = $value;
}
/**
* Convert the object to its JSON representation.
*
* @param int $options
* @return string
*/
public function toJson($options = 0)
{
return json_encode($this);
}
/**
* Get the instance as an array.
*
* @return array
*/
public function toArray()
{
return $this->attributes;
}
/**
* Specify data which should be serialized to JSON
* @link https://php.net/manual/en/jsonserializable.jsonserialize.php
* @return mixed data which can be serialized by <b>json_encode</b>,
* which is a value of any type other than a resource.
* @since 5.4.0
*/
public function jsonSerialize()
{
return $this->attributes;
}
}
Modify this as you wish, but you shouldn't serialize $id property
2. Create UserProvider
<?php namespace App\Extensions;
use App\Models\Session;
use Illuminate\Cache\Repository;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Contracts\Auth\UserProvider;
use Illuminate\Support\Fluent;
use Illuminate\Support\Str;
class SessionUserProvider implements UserProvider
{
private $store;
/**
* SessionUserProvider constructor.
* @param Repository $store
*/
public function __construct(Repository $store)
{
$this->store = $store;
}
/**
* Retrieve a user by their unique identifier.
*
* @param mixed $identifier
* @return \Illuminate\Contracts\Auth\Authenticatable|null
*/
public function retrieveById($identifier)
{
return new Session(
$this->getUniqueTokenForSession($identifier)
);
}
/**
* Retrieve a user by their unique identifier and "remember me" token.
*
* @param mixed $identifier
* @param string $token
* @return \Illuminate\Contracts\Auth\Authenticatable|null
*/
public function retrieveByToken($identifier, $token)
{
return null;
}
/**
* Update the "remember me" token for the given user in storage.
*
* @param \Illuminate\Contracts\Auth\Authenticatable $user
* @param string $token
* @return void
*/
public function updateRememberToken(Authenticatable $user, $token)
{
return;
}
/**
* Retrieve a user by the given credentials.
*
* @param array $credentials
* @return \Illuminate\Contracts\Auth\Authenticatable|null
*/
public function retrieveByCredentials(array $credentials)
{
return null;
}
private function unpack($data)
{
return json_decode($data);
}
private function getUniqueTokenForSession($id)
{
return $this->retrieveCacheDataForSession($id)
->get('uuid');
}
private function retrieveCacheDataForSession($id)
{
$fluent = new Fluent(
$this->unpack(
$this->store->has($id) ? $this->store->get($id) : "[]"
)
);
if(!$fluent->__isset('uuid')) {
$fluent->__set('uuid', Str::random(128));
}
$this->store->put($id, $fluent->toJson(), 60 * 60 * 60);
return $fluent;
}
/**
* Validate a user against the given credentials.
*
* @param \Illuminate\Contracts\Auth\Authenticatable $user
* @param array $credentials
* @return bool
*/
public function validateCredentials(Authenticatable $user, array $credentials)
{
return null;
}
}
Identifier property in retrieveById method is always session id if you are using broadcasting so you can also use this as a token.
3. Create new Guard
<?php namespace App\Services\Auth;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Contracts\Auth\Guard;
use Illuminate\Contracts\Auth\UserProvider;
use Illuminate\Http\Request;
class GuestGuard implements Guard
{
private $user;
protected $request;
protected $provider;
/**
* GuestGuard constructor.
* @param UserProvider $provider
* @param Request $request
*/
public function __construct(UserProvider $provider, Request $request)
{
$this->provider = $provider;
$this->request = $request;
}
/**
* Determine if the current user is authenticated.
*
* @return bool
*/
public function check()
{
return !is_null($this->user);
}
/**
* Determine if the current user is a guest.
*
* @return bool
*/
public function guest()
{
return !$this->check();
}
/**
* Get the currently authenticated user.
*
* @return \Illuminate\Contracts\Auth\Authenticatable|null
*/
public function user()
{
if($this->check()) {
return $this->user;
}
$this->setUser(
$this->provider->retrieveById(
$this->request->session()->getId()
)
);
return $this->user;
}
/**
* Get the ID for the currently authenticated user.
*
* @return int|null
*/
public function id()
{
return !is_null($this->user) ? $this->user->id : null;
}
/**
* Validate a user's credentials.
*
* @param array $credentials
* @return bool
*/
public function validate(array $credentials = [])
{
return false;
}
/**
* Set the current user.
*
* @param \Illuminate\Contracts\Auth\Authenticatable $user
* @return void
*/
public function setUser(Authenticatable $user)
{
$this->user = $user;
}
}
Here in user method you pass session id as identifier, using broadcasting only this method is nessesary.
4. Register Guard and UserProvider in AuthServiceProvider.
// app/Providers/AuthServiceProvider.php
/**
* Register any authentication / authorization services.
*
* @return void
*/
public function boot()
{
$this->registerPolicies();
Auth::provider('sessions', function (Application $app) {
return new SessionUserProvider(
$app->make('cache.store')
);
});
Auth::extend('guest', function (Application $app, $name, array $config) {
return new GuestGuard(Auth::createUserProvider($config['provider']), $app->make('request'));
});
}
5.1 Add provider in config/auth.php
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\User::class,
],
// New
'sessions' => [
'driver' => 'sessions',
'model' => App\Models\Session::class,
],
],
5.2 Add guard in config/auth.php
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'token',
'provider' => 'users',
'hash' => false,
],
// New
'guest' => [
'driver' => 'guest',
'provider' => 'sessions'
]
],
6. Use your new guard
// routes/channels.php
Broadcast::channel('chat.{id}', function (Authenticatable $user){
return $user;
}, ['guards' => ['guest']]);
Notice that you can use 'web' as a guard at the same time ('web' should be before 'guest'). It allows you to find out who is a guest and who is a logged in user - you can just check instance of Authenticable in channel callback.
And that how it looks in the laravel-echo-server database