5 min read

Livewire's New #[Authorize] Attribute: Declarative Authorization for Actions

Livewire 4.3 added a declarative #[Authorize] attribute for actions, closing a common gap. How it works, the fixes since, and where it still falls short.

Featured image for "Livewire's New #[Authorize] Attribute: Declarative Authorization for Actions"

Livewire’s own security documentation opens with a warning worth repeating: any parameter passed into a Livewire action is mutable on the client and should be treated as untrusted input. A wire:click="delete({{ $post->id }})" binding looks safe in the Blade template, but nothing stops a user from editing that call in their browser’s dev tools and sending a different ID. For years, the fix has been the same one Laravel controllers use: call $this->authorize() or Gate::authorize() inside the action body before touching the database. It works, but it is easy to forget, and the check lives buried inside the method instead of announced at the top of it.

Livewire 4.3.0, released May 1, 2026, added a way to lift that check out of the method body entirely: a declarative #[Authorize] attribute for actions, contributed by Perry van der Meer in PR #10089. It mirrors the authorization attribute Laravel’s own framework picked up not long before (laravel/framework #59048), and it has been refined in a handful of point releases since.

Four ways to use it

The simplest form is a plain Gate check with no model involved, useful for permissions rather than resource ownership:

use Livewire\Attributes\Authorize;

#[Authorize('view-dashboard')]
public function showStats()
{
    // ...
}

For “create” actions, where no instance exists yet to authorize against, pass the class name:

#[Authorize('create', Post::class)]
public function create()
{
    // ...
}

The more interesting cases are the ones that resolve a model automatically. If the component already has a typed public property, reference it by name as a string:

class EditPost extends Component
{
    public Post $post;

    #[Authorize('update', 'post')]
    public function save()
    {
        $this->post->update($this->only(['title', 'body']));
    }
}

And if the model should come from the action’s own parameters instead, including through Laravel’s route model binding, the attribute resolves that too:

#[Authorize('delete', 'comment')]
public function delete(Comment $comment)
{
    $comment->delete();
}

In every case, if the current user fails the policy check, Livewire throws an AuthorizationException before the method body runs at all. There is no path through the action that skips it, which is the entire point of moving the check out of userland code.

Property versus parameter: the resolution order matters

During review, Caleb Porzio raised a reasonable question: what happens when the second argument to #[Authorize] could refer to either a method parameter or a component property with the same name? The team settled on parameter-first resolution, falling back to the property only if no matching parameter exists. If you have both a $comment property and a $comment parameter on the same action, the attribute authorizes against whatever was passed into the method, not the stored property. Worth knowing if you have components that keep a model cached as state and also accept one as an override.

The merged PR also closed a real bypass: authorization checks now run in Livewire’s event listener dispatch path before the listener method executes, not just on direct action calls. Without that fix, dispatching an event at a component could have skipped the attribute entirely.

The fixes that followed

#[Authorize] shipped with a rough edge. Laravel’s own authorize() method has long supported passing an array of arguments for “additional context” to a policy method, beyond the single model instance. The initial implementation of the attribute did not carry that over, which meant policies written to expect extra context would break when called through the attribute instead of manually. PR #10260, merged May 8 and shipped in Livewire v4.3.1, added array support to bring the attribute in line with Laravel’s authorization documentation.

A second round of argument-resolution fixes landed in v4.3.3 on June 27, tightening how the attribute figures out which value to authorize against when the property-versus-parameter logic produced the wrong answer in edge cases. If you adopted #[Authorize] the day it shipped in 4.3.0, it is worth updating to at least 4.3.3 before relying on it heavily, since both the additional-context support and the resolution fixes are real behavior changes, not just documentation updates.

Where it fits in the bigger picture

#[Authorize] does not replace the rest of Livewire’s authorization surface, it adds a cleaner entry point for the most common case. Livewire’s security documentation still recommends the #[Locked] attribute for public properties that should never be tampered with from the browser, and persistent middleware for route-level authorization that needs to survive across a component’s subsequent network requests. None of that changes. What #[Authorize] buys you is a declarative alternative for the specific, extremely common pattern of “check a policy before running this action,” in the same spirit as attributes like #[Validate] and #[Computed] that Livewire has favored since v3.

For teams running a lot of Livewire components with delete, update, or restricted-view actions, this is a small change with an outsized readability payoff. A reviewer scanning a component class can now see the authorization requirement in the method signature instead of hunting for a $this->authorize() call three lines into the body, and a missing check is now a visible gap in the attribute list rather than a silent omission.

Upgrading

#[Authorize] requires Livewire 4.3.0 or later. The current release as of this writing is v4.3.5, which requires PHP 8.1 or later and supports Laravel 10 through 13. If you are still on the Livewire 3.x line, none of this applies yet; the attribute is 4.x only, and there is no indication it is being backported.

Sources