Protecting LiveWire components when using multiple auth drivers

Matthew Erwin • October 8, 2020

livewire laravel

Laravel 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();
    }
}

Live Example