yougotnet's avatar

Laravel Policy and Spatie Permissions

I want to validate that a user can edit post that has the user id on it. I am trying to use Laravel Policies but I have Spatie Permissions installed and the policy never runs.

I've created a spatie permission to edit post, but that doesn't check the user id.

0 likes
4 replies
LaryAI's avatar
Level 58

Certainly! Here’s a solution to the Laracasts forum question. The main issue is that Spatie Permissions' can method short-circuits the policy if there’s a matching permission, so your policy is never triggered. If you want to check both the permission and the user-specific logic in the policy, here’s how to achieve that:


Solution:

First, define your policy with the extra user check, e.g. PostPolicy:

public function update(User $user, Post $post)
{
    // Only allow if user has permission AND owns the post
    return $user->can('edit post') && $user->id === $post->user_id;
}

Then, make sure it’s registered in AuthServiceProvider:

protected $policies = [
    \App\Models\Post::class => \App\Policies\PostPolicy::class,
];

When you check the authorization in your controller or route, use the authorize method or policy gate:

public function update(Request $request, Post $post)
{
    $this->authorize('update', $post);
    // ... update logic
}

Important:
If you call $user->can('edit post') directly, Spatie’s permission trait will return true if the user has the permission without running your policy logic. Always use the policy call ($this->authorize() or Gate::allows()) to enforce the extra logic.

If you want the policy to be evaluated every time (and not short-circuited by Spatie), avoid assigning edit post as a permission directly on the user/role and let the policy handle the check.
Or, if you want finer control, you can modify the policy method to check for both the permission and the user match, as in the example above.


Summary:

  • Write an explicit policy for your model (PostPolicy).
  • Register it in AuthServiceProvider.
  • Use $this->authorize('update', $post) in your controller—not $user->can('edit post') directly.
  • In your policy, combine permission and ownership checks as needed.

Let me know if you need a more advanced example!

jlrdw's avatar

The docs explain this: https://laravel.com/docs/13.x/authorization#policy-methods

in example see this line:

return $user->id === $post->user_id;

In words: If the logged in users id matches the users id of that post is true they can edit. If not they cannot edit.

I suggest another study of policies. https://laravel.com/docs/13.x/authorization#creating-policies

You can also use RBAC see https://martinbean.dev/blog/2021/07/29/simple-role-based-authentication-laravel/

I use something similar. Once a good RBAC system is learned (yes high learning curve) an outside package isn't needed.

Edit:

You can also use gates instead of policies.

Also security can be one of the trickiest aspects of an app to setup. Never depend on AI for coding correct security.

But once learned well, it gets easier.

Please or to participate in this conversation.