Protecting LiveWire components when using multiple auth drivers
Matthew Erwin • October 8, 2020
livewire laravelLaravel Livewire provides a dead easy way to add dynamic components to your site without the need to write any JavaScript. Although Livewire does support authentication out of the box, if you have multiple auth guards, you can run into issues or risk leaving your routes unprotected.
The Problem
You can protect LiveWire components using middleware (https://laravel-livewire.com/docs/authorization) but the middleware applies accross all components. What if you have some front-end and some admin components that require different auth guards?
The Solution
We can check the specific auth guard within the constructor of the component.
<?php
namespace App\Http\Livewire;
use Livewire\Component;
class Counter extends Component
{
public function __construct()
{
abort_if(\Auth::guard('admin')->check(), 401);
}
public function render()
{
return view('livewire.counter');
}
}
To re-use this for multiple components, we could move this logic to a trait.
We can use the initialize{ClassName}
method naming convention to auto boot this method.
<?php
namespace App\Traits\Livewire;
trait GuardsAgainstAccess
{
public function initializeGuardsAgainstAccess()
{
if (\App::runningInConsole() && !\App::runningUnitTests()) {
return;
}
if (isset($this->guard)) {
abort_unless(\Auth::guard($this->guard)->check(), 401);
}
}
}
And use it in each component.
<?php
namespace App\Http\Livewire;
use App\Traits\Livewire\GuardsAgainstAccess;
use Livewire\Component;
class Counter extends Component
{
use GuardsAgainstAccess;
protected $guard = 'admin';
public function render()
{
return view('livewire.counter');
}
}
Testing
We can test the component by performing an assertion on the HTTP status code when the component is rendered.
<?php
namespace Tests\Feature;
use App\Models\Administrator;
use App\Http\Livewire\Counter;
use Tests\TestCase;
class LivewireRouteGuardTest extends TestCase
{
public function testLivewireComponentGuard()
{
$livewire = \Livewire::test(Counter::class);
$livewire->lastResponse->assertStatusCode(401);
$this->actingAs(factory(Administrator::class)->make(), 'admin');
$livewire = \Livewire::test(Counter::class);
$livewire->lastResponse->assertOk();
}
}