Unauthorized access when using policies and authorizeResource helper

Laravel policies are a great way to protect your resources without cluttering your controllers with all sorts of conditionals. I set up a policy to ensure the following:

- Guests are only allowed to view published posts

- Authenticated users are able to view all posts

- Authenticated users are also allowed to create, edit, and delete posts

I wrote a policy class for these rules (abbreviated example):

class PostPolicy {
    use HandlesAuthorization;

    /**
     * Determine whether the user can view the post.
     *
     * @param  \App\User  $user
     * @param  \App\Post  $post
     * @return mixed
     */
    public function view(?User $user, Post $post) {
        // Either the post must be published or
        // the user must be authenticated.
        return $post->isPublished || filled($user);
    }

    /**
     * Determine whether the user can create posts.
     *
     * @param  \App\User  $user
     * @return mixed
     */
    public function create(User $user) {
        return $user->exists;
    }

    /**
     * Determine whether the user can update the post.
     *
     * @param  \\App\\User  $user
     * @param  \\App\\Post  $post
     * @return mixed
     */
    public function update(User $user, Post $post) {
        return $user->exists;
    }
}

In order to protect the resourceful controller, Laravel provides a helper function (authorizeResource) that can be placed in the constructor. However, although I was authenticated, I kept hitting a 403 unauthorized error when attempting to show or edit a post.

It turned out, the solution was simple: don't forget to use model binding in your resourceful controller. So instead of using int $id in the show / edit methods, use Post $post like this:

class PostsController extends Controller {
    public function __construct() {
        $this->authorizeResource(Post::class);
    }

    // Use Post $post instead of int $id, otherwise policies won't work!
    public function show(Post $post) {
        return view('posts.show', compact('post'));
    }

    // Use Post $post instead of int $id, otherwise policies won't work!
    public function edit(Post $post) {
        return view('posts.edit', compact('post'));
    }
}

After this, everything worked like a charm!

Clicky