Laravel: Being explicit in authorization policies

I’m working on a Laravel application right now and I got myself in a bit of a tangle about authorization policies because of an undocumented way of using them.

Overview of policies

In Laravel, Policies are a way of specifying who can take particular actions. In this application we are using resource controllers (with typical resource methods like index, create, store, etc) and manually authorizing in the controllers using the controller helper method described in the documentation.

$this->authorize('create', Post::class);

or, for methods that require authorization for a particular object/model:

public function update(Request $request, Post $post)
    $this->authorize('update', $post);

The problem

The problem I had here is that in the latter example, it’s kinda unclear which policy is being used. In the first example above it’s explicit – we’re using using the PostPolicy because we passed in Post::class to specify which model we are using.

In the second example the Policy is not specified – I’m guessing that the class of the type-hinted Post is used to determine which policy is called. But it’s not clear especially when…

The use case – and why the problem is a problem

In my case I’m authorizing users to edit users, so my controllers looked like:

public function update(Request $request, User $user)
    $this->authorize('update', $user);

But then I wanted some child classes of the User model, which look like:

class SuperAdmin extends User
    protected $table = 'users';

Now, if a SuperAdmin is passed to the UserController, which Policy is used? And – I know I probably shouldn’t but – if I wanted to use a specific policy here, what do I do? My only option is to change the type hint, isn’t it? And I may not want to do that?

The solution!

Well, no. It’s not the only option. It turns out you can pass an array to the authorize helper which seems to let you specify both the object you are trying to authorize for, and a specific class/policy:

$this->authorize('update', [SuperAdmin::class, $user]);

So yay! Now, there’s probably a whole bunch of reasons you wouldn’t/shouldn’t do this, but that’s not the point. I want to remember that you can choose a policy by specifying a class name. And…job done!