Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

zionraiden's avatar

Laravel 11 Policy always returns 403

why does my controller actions always returns a 403 even if the policy returns true?

I'm using Laravel 11, I have BoardItemFile model and BoardItemFilePolicy. Even if the conditions are met, it still returns 403.

What I did:

  • tried directly returning true on the policy method - still fails
  • manually registering the policy in AppServiceProvider - still fails

my policy:

class BoardItemFilePolicy
{
    public function before(User $user, string $ability) : bool | null {
        if ($user->userRole->role == 0) {
            return true;
        }

        return null;
    }

    public function create(User $user, BoardItem $item): bool
    {
        return true;
       // return $user->id == $item->user_id;
    }

and the controller method:

    public function store(Workspace $workspace, Board $board, BoardItem $item, Request $request)
    {
        if ($request->user()->cannot('create', $item)) {
            abort(403);
        }

I also tried Gate::authorize('create', [$item]) and returns the same thing.

I'm not sure why this particular policy fails as I have more policy which works fine and expected.

0 likes
6 replies
vincent15000's avatar

The policies are automatically declared and attached to the right model if you apply strictly the naming convention.

Model / ModelPolicy BoardItem / BoardItemPolicy and not BoardItemFilePolicy

2 likes
zionraiden's avatar

@vincent15000 this is actually for the BoardItemFileController.

this is the route with the bindings:

 GET|HEAD        api/workspaces/{workspace}/boards/{board}/items/{item}/files ... workspaces.boards.items.files.index › BoardItemFileController@index  
  POST            api/workspaces/{workspace}/boards/{board}/items/{item}/files ... workspaces.boards.items.files.store › BoardItemFileController@store  
  DELETE          api/workspaces/{workspace}/boards/{board}/items/{item}/files/{file} workspaces.boards.items.files.destroy › BoardItemFileController… 

Controller:

class BoardItemFileController extends Controller
{
    public function index(Workspace $workspace, Board $board, BoardItem $item)
    {
        $boardItemFiles = $item->files;
        return response()->json(["data" => $boardItemFiles]);
    }

    /**
     * Store a newly created resource in storage.
     */
    public function store(Request $request, Workspace $workspace, Board $board, BoardItem $item)
    {
        // dd($item, $request->user());
        if ($request->user()->cannot('create', $item)) {
            abort(403);
        }
        $request->validate([
            "files" => ['required', 'array'],
            "files.*" => ['file', 'image', 'max:10240']
        ]);
        ...
1 like
jlrdw's avatar

Agreed, try reworking the code to:

public function store(Workspace $workspace, Board $board, BoardItem $boardItem, Request $request)

And double check examples in docs.

2 likes
zionraiden's avatar

@jlrdw to confirm, the changes made is only for the variable binding name? I've checked the route list and it gives the item name instead.

  GET|HEAD        api/workspaces/{workspace}/boards/{board}/items/{item}/files ... workspaces.boards.items.files.index › BoardItemFileController@index  
  POST            api/workspaces/{workspace}/boards/{board}/items/{item}/files ... workspaces.boards.items.files.store › BoardItemFileController@store  
  DELETE          api/workspaces/{workspace}/boards/{board}/items/{item}/files/{file} workspaces.boards.items.files.destroy › BoardItemFileController…
1 like
jaseofspades88's avatar

Route model binding has nothing to do with the policies. @vincent15000 suggested the naming conventions between your model and your policy are not explicit, which is why the policy is not being hit. If you explicitly bind the policy to the model, first check you're not tripping yourself up by hitting the before method in said policy. Put a simple dd in your before method until you're able to successfully render said dd when making a request.

2 likes

Please or to participate in this conversation.