Framework: Authorizing Resource Controllers doesn't work for the actions that requires model's ID

Created on 16 Apr 2020  路  9Comments  路  Source: laravel/framework


  • Laravel Version: 7.4.0
  • PHP Version: 7.4.1
  • Database Driver & Version: MySQL 5.7.28

Description:

Authorizing Resource Controllers doesn't work for the actions that requires route / request parameter that will contain the model's ID. In that cases Laravel always throws unauthorized exception.

Steps To Reproduce:

Follow sample from the documentation in section Authorizing Resource Controllers of https://laravel.com/docs/7.x/authorization#via-controller-helpers

<?php

namespace App\Http\Controllers;

use App\Http\Controllers\Controller;
use App\Post;
use Illuminate\Http\Request;

class PostController extends Controller
{
    public function __construct()
    {
        $this->authorizeResource(Post::class, 'post');
    }
}

Make policy via
php artisan make:policy PostPolicy --model=Post

Set true in all policy methods.

Having resource route in api.php
Route::resource('post', 'PostController');

Having Post model with ID 1

Make GET request /post/1

Expected result:
See the response from PostController::show().

Actual result:
403 This action is unauthorized.

Most helpful comment

I finally figured out what was "wrong" with my code.

I created the controller via artisan command php artisan make:controller PostController -api
There are (int $id) arguments in all of the PostController methods show/update/delete by default.

public function show($id) {
    //
}

In order to use Authorizing Resource Controllers feature it's crucial to explicitly bind model to those methods argument instead of $id.

public function show(Post $post) {
    //
}

This is pretty much inobvious and worth mentioning in the Authorizing Resource Controllers documentation.

The better solution would be implementing Authorizing Resource Controllers without need of explicit model binding in controller actions. It should work out of the box as expected.

All 9 comments

@Dalamar
Policies require auth user.
More if the policy method is empty (no return) you are not authorize.

In case user is not auth perhaps laravel should return 401 status code.

@Oipnet It's 403 not 401 status code.
In the described case request to the API is authenticated.

Works just fine for me. Did you set the policy in the AuthServiceProvider?

@driesvints
Of course I set the policy in the AuthServiceProvider.

Please take to the consideration that authorization works fine in this resource controller via
$this->authorize('update', $post);

The problem occurs only using $this->authorizeResource(Post::class, 'post'); in the constructor.

I tried it by placing it in the constructor.

I finally figured out what was "wrong" with my code.

I created the controller via artisan command php artisan make:controller PostController -api
There are (int $id) arguments in all of the PostController methods show/update/delete by default.

public function show($id) {
    //
}

In order to use Authorizing Resource Controllers feature it's crucial to explicitly bind model to those methods argument instead of $id.

public function show(Post $post) {
    //
}

This is pretty much inobvious and worth mentioning in the Authorizing Resource Controllers documentation.

The better solution would be implementing Authorizing Resource Controllers without need of explicit model binding in controller actions. It should work out of the box as expected.

Not to mention that explicit model binding in controller actions arguments prevents using the Repository pattern which I use in all of my controllers.

@Dalamar feel free to send in a PR to the docs 馃憤

Was this page helpful?
0 / 5 - 0 ratings